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: bad access to attributes with acquisition wrapper value #48

Closed d-maurer closed 3 years ago

d-maurer commented 3 years ago

The problem is demonstrated by the following code:

>>> from Acquisition import Implicit
>>> class I(Implicit):
...   def add(self, id, o):
...     setattr(self, id, o)
...     return getattr(self, id)
... 
>>> top = I()
>>> f = top.add("f", I())
>>> 
>>> i = I()
>>> i.add("f", f)
<__main__.I object at 0x7f7b8acbf668>
>>> i.f.aq_self is f
False

Works in the "C" version.

The problem is caused by Acquisition._Wrapper.__of__:

    def __of__(self, parent):
        # Based on __of__ in the C code;
        # simplify a layer of wrapping.

        # We have to call the raw __of__ method or we recurse on our
        # own lookup (the C code does not have this issue, it can use
        # the wrapped __of__ method because it gets here via the
        # descriptor code path)...
        wrapper = self._obj.__of__(parent)
        if (not isinstance(wrapper, _Wrapper) or
                not isinstance(wrapper._container, _Wrapper)):
            return wrapper
        # but the returned wrapper should be based on this object's
        # wrapping chain
        wrapper._obj = self

        while (isinstance(wrapper._obj, _Wrapper) and
               (wrapper._obj._container is wrapper._container._obj)):
            # Since we mutate the wrapper as we walk up, we must copy
            # XXX: This comes from the C implementation. Do we really need to
            # copy?
            wrapper = type(wrapper)(wrapper._obj, wrapper._container)
            wrapper._obj = wrapper._obj._obj
        return wrapper

It fails, if parent is not a wrapper.

d-maurer commented 3 years ago

Fixed by #49