[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.
Cheers,
Yuppie
More information about the Zope-CMF
mailing list