zopefoundation / Acquisition

Acquisition is a mechanism that allows objects to obtain attributes from the containment hierarchy they're in.
Other
12 stars 12 forks source link

Pure-Python Wrapper objects break `object.__getattribute__` in methods of the wrapped object #9

Closed jamadden closed 9 years ago

jamadden commented 9 years ago

Certain "base" classes make direct use of object.__getattribute__ in their method implementations. (i.e., outside of their own __getattribute__ implementation). They may do this for performance reasons, or to simplify their implementation of __getattribute__. One prominent example is persistent.Persistent, but there are undoubtedly others.

If classes like this are wrapped with a pure-Python acquisition wrapper, those methods fail, because the self they have is not the self they expect (it's the wrapper), and directly calling object.__getattribute__ bypasses the wrapper's __getattribute__ implementation.

Here's an example test that fails with pure-Python, but works under the C implementation:

    def test_object_getattribute_in_rebound_method(self):

        class Persistent(object):
            __slots__ = ('__flags')
            def __init__(self):
                self.__flags = 42

            def get_flags(self):
                return object.__getattribute__(self, '_Persistent__flags')

        wrapped = Persistent()
        wrapper = Acquisition.ImplicitAcquisitionWrapper(wrapped, None)

        self.assertEqual(wrapped.get_flags(), wrapper.get_flags())

It fails like so:

  File "/Acquisition/tests.py", line 3171, in test_object_getattribute_in_rebound_method
    self.assertEqual(wrapped.get_flags(), wrapper.get_flags())
  File "/Acquisition/tests.py", line 3166, in get_flags
    return object.__getattribute__(self, '_Persistent__flags')
AttributeError: 'ImplicitAcquisitionWrapper' object has no attribute '_Persistent__flags'

I'm trying to figure out how to fix this.

dwt commented 8 years ago

Hi @jamadden while trying to port this to python 3 we noticed that the combination of pure python Acquisition, AccessControll and RestrictedPython together breaks at this point.

The problem as best as I can describe it is, that there is no way to do a perfect proxy in python. In this case, that means that one cannot get at the __dict__ of a class object in python 2.7, but always gets a dict_proxy instead, which then cannot be assigned as the __dict__ of another instance.

So, can you please explain more about the use case in which this arose? We (at the Zope Resurrection Sprint in Halle) suspect that supporting object.__getattribute__ may be something that does / should not happen outside of Zope itself - so can you name more use cases?

To be more specific, we think that also the pure python implementation of persistant.Persistant is probably something we cannot support in the future (especially since there is a working C implementation that also works for python3).

So, can you please give some updates?

dwt commented 8 years ago

@hannosch Was instrumental in formulating this bug report, so I'm pinging him here.

jamadden commented 8 years ago

The use case is PyPy. C extensions don't work there, so a pure-Python Acquisition and persistent are used together. This is necessary for PyPy to work.