[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