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)
            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.



Zope-CMF maillist  -  Zope-CMF@lists.zope.org

See http://collector.zope.org/CMF for bug reports and feature requests

Reply via email to