[Zope-CMF] Re: How do deal with cmfcatalog-wrapped objects?

yuppie y.2006_ at wcm-solutions.de
Sun Apr 2 17:17:30 EDT 2006

Hi Philipp!

Philipp von Weitershausen wrote:
> yuppie wrote:
>> The quick and dirty solution would be to remove the interface
>> declaration from the wrapper. The clean solution would be to make sure
>> that all the interfaces that are actually provided - the wrapper
>> interface *and* the interfaces of the wrapped object - can be looked up.
>> But implementing that seems to require deeper knowledge of the interface
>> machinery than I have.
> This problem has already been solved in Zope 3. There we like to wrap
> objects that don't provide ILocation (__parent__ and __name__
> attributes) in something that *does* provide ILocation. The resulting
> object is a proxy for the original object and in addition that it
> provides __parent__ and __name__ attributes. The proxy provides whatever
> the original object provides plus ILocation.
> We call this concept a /decorator/. This is not to be confused with
> Python 2.4's function decorators. In Zope 3's case, think of decorator
> as a proxy that also adds stuff to the object (e.g. the ILocation API).
> Hence, it decorates the original object, like a Christmas tree if you will.

Great! Good to know.

> There are two options:
> 1. I think for the long term, IndexableObjectWrapper could be made a
> decorator. This works as follows:
>   from zope.proxy import getProxiedObject
>   from zope.app.decorator import Decorator
>   class IndexableObjectWrapper(Decorator):
>       def allowedRolesAndUsers(self):
>           ob = getProxiedObject(self)
>           allowed = {}
>           for r in rolesForPermissionOn(View, ob):
>               allowed[r] = 1
>           localroles = _mergedLocalRoles(ob)
>           for user, roles in localroles.items():
>               for role in roles:
>                   if allowed.has_key(role):
>                       allowed['user:' + user] = 1
>           if allowed.has_key('Owner'):
>               del allowed['Owner']
>           return list(allowed.keys())

Why is this your preferred long term option? IndexableObjectWrapper does 
a lot of special stuff. It doesn't just proxy to an object and add a new 
method. It also allows to pass in a list of vars that are looked up 
first by __getattr__. To get this working I did have to provide __new__, 
__init__ and __getattr__ as well. So the base class doesn't make the 
code much simpler.

> 2. In the short term we can apply the following trick
> (IndexableObjectWrapper needs to be a new style class!):
>   from zope.interface import providedBy
>   from zope.interface.declarations import ObjectSpecificationDescriptor
>   from zope.interface.declarations import getObjectSpecification
>   from zope.interface.declarations import ObjectSpecification
>   class IndexableObjectSpecification(ObjectSpecificationDescriptor):
>       def __get__(self, inst, cls=None):
>           if inst is None:
>               return getObjectSpecification(cls)
>           else:
>             provided = providedBy(inst.__ob)
>             cls = type(inst)
>             return ObjectSpecification(provided, cls)
>   class IndexableObjectWrapper(object):   # new-style!
>       implements(...)  # it can implement as much as it wants
>       __providedBy__ = IndexableObjectSpecification()
>       ...
> This is obviously untested, but I'm pretty confident that this would work.

Yeah, works with a small modification: '__ob' has double leading 
underscores, so I did have to use '_IndexableObjectWrapper__ob' instead.

And switching to a new style class has a side effect: 'object' has some 
attributes that now mask the attributes of the wrapped object. AFAICS 
this is only a problem with '__str__' that is the default method for 
getting the binary data of a file object.

I checked in a fix using the second option.



More information about the Zope-CMF mailing list