[Zope3-dev] Backward-incompatible bug fix to zope.proxy

Jim Fulton jim at zope.com
Mon Apr 3 05:59:27 EDT 2006


Roger Ineichen wrote:
> Jim Fulton schrieb:
> 
>> A while ago, Gary and I found what appeared to be a bug in
>> zope.proxy.ProxyBase's handling of non-data descriptors (descriptors
>> that define __get__, but not __set__ and __delete__) defined in
>> proxy classes.  Normally, when a class has non-data descriptors,
>> instance data overrides the descriptor.  ProxyBase let non-data
>> descriptors override instance data.  For a proxy, instance data almost
>> always comes from the proxied object.  This behavior of ProxyBase
>> caused subtle bugs.
>>
>> I've decided to fix this bug because it was causing my pain on my
>> jim-adapter branch.  I decided to fix this bug on the branch.  The fix
>> has a somewhat unexpected side effect. It turns out that some proxy
>> applications depended on the old behavior.
>>
>> Consider location proxies:
>>
>>   class LocationProxy(ProxyBase):
>>       ...
>>
>>       def __reduce__(self, proto=None):
>>           raise TypeError("Not picklable")
>>
>>       ...
>>
>> Here the proxy is trying to prevent pickling by providing a __reduce__
>> that raises an exception.  Now methods are non-data descriptors.  With
>> the fix, the __reduce__ of the proxied object is used.  We need to
>> convert __reduce__ to a data descriptor.  I've added a function to
>> the branch that can be used as a decorator to do this:
>>
>>
>>   class LocationProxy(ProxyBase):
>>       ...
>>
>>       @zope.proxy.non_overridable
>>       def __reduce__(self, proto=None):
>>           raise TypeError("Not picklable")
>>
>>       ...
>>
>>
>> I also had to change the the descriptors defined in zope.app.decorator
>> to be data descriptors.
>>
>> The fix is obviously not backward compatible.  I don't know if this
>> would effect anything outside of Zope.  If this affects anyone, please
>> let me know.  If necessary, I can probably provide a ProxyBase2 with
>> the fix and leave ProxyBase alone, but I'd rather not.
>>
>> Is anyone using zope.proxy to define custom proxy types?
>>
>> Jim
>>
> 
> Hi Jim,
> 
> We just use a IContainer location proxy adapter.
> But since this adapter isn't persistent I don't think this
> is a problem.
> 
> def proxify(container, item):
>     if IContainer.providedBy(item):
>         proxy = ContainerLocationProxy(item)
>     else:
>         proxy = LocationProxy(item)
>     proxy.__name__ = item.__name__
>     proxy.__parent__ = container
>     return proxy
> 
> class ContainerLocationProxy(LocationProxy):
>     """Proxy the location of a container an its items."""
> 
>     # zope.app.conatiner.interfaces.IReadContainer
>     def __getitem__(self, key):
>         return proxify(self, getProxiedObject(self).__getitem__(key))
> 
>     ...

Well, with the fix, your __getitem__ won't be called. Is that a
problem? ;)

You will need to use the @non_overridable decorator on your __getitem__
function.

Jim

-- 
Jim Fulton           mailto:jim at zope.com       Python Powered!
CTO                  (540) 361-1714            http://www.python.org
Zope Corporation     http://www.zope.com       http://www.zope.org


More information about the Zope3-dev mailing list