diff --git a/CHANGES.rst b/CHANGES.rst
index 3e617c3..0b44a8b 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -4,7 +4,15 @@ Changelog
 4.2.2 (unreleased)
 ------------------
 
-- TBD
+- Make the pure-Python Acquirer objects cooperatively use the
+  superclass ``__getattribute__`` method, like the C implementation.
+  See https://github.com/zopefoundation/Acquisition/issues/7.
+
+- The pure-Python implicit acquisition wrapper allows wrapped objects
+  to use ``object.__getattribute__(self, name)``. This differs from
+  the C implementation, but is important for compatibility with the
+  pure-Python versions of libraries like ``persistent``. See
+  https://github.com/zopefoundation/Acquisition/issues/9.
 
 4.2.1 (2015-04-23)
 ------------------
diff --git a/src/Acquisition/__init__.py b/src/Acquisition/__init__.py
index eae7d7c..c44bbba 100644
--- a/src/Acquisition/__init__.py
+++ b/src/Acquisition/__init__.py
@@ -2,10 +2,12 @@ from __future__ import absolute_import, print_function
 
 # pylint:disable=W0212,R0911,R0912
 
+
 import os
 import operator
 import sys
 import types
+import weakref
 
 import ExtensionClass
 
@@ -39,17 +41,30 @@ def _apply_filter(predicate, inst, name, result, extra, orig):
     return predicate(orig, inst, name, result, extra)
 
 if sys.version_info < (3,):
+    import copy_reg
+
     def _rebound_method(method, wrapper):
         """Returns a version of the method with self bound to `wrapper`"""
         if isinstance(method, types.MethodType):
             method = types.MethodType(method.im_func, wrapper, method.im_class)
         return method
+    exec("""def _reraise(tp, value, tb=None):
+    raise tp, value, tb
+""")
 else: # pragma: no cover (python 2 is currently our reference)
+    import copyreg as copy_reg
+
     def _rebound_method(method, wrapper):
         """Returns a version of the method with self bound to `wrapper`"""
         if isinstance(method, types.MethodType):
             method = types.MethodType(method.__func__, wrapper)
         return method
+    def _reraise(tp, value, tb=None):
+        if value is None:
+            value = tp()
+        if value.__traceback__ is not tb:
+            raise value.with_traceback(tb)
+        raise value
 
 ###
 # Wrapper object protocol, mostly ported from C directly
@@ -283,16 +298,58 @@ def _Wrapper_findattr(wrapper, name,
 
 
 _NOT_GIVEN = object()  # marker
+_OGA = object.__getattribute__
+
+# Map from object types with slots to their generated, derived
+# types (or None if no derived type is needed)
+_wrapper_subclass_cache = weakref.WeakKeyDictionary()
+
+def _make_wrapper_subclass_if_needed(cls, obj, container):
+    # If the type of an object to be wrapped has __slots__, then we
+    # must create a wrapper subclass that has descriptors for those
+    # same slots. In this way, its methods that use object.__getattribute__
+    # directly will continue to work, even when given an instance of _Wrapper
+    if getattr(cls, '_Wrapper__DERIVED', False):
+        return None
+    type_obj = type(obj)
+    wrapper_subclass = _wrapper_subclass_cache.get(type_obj, _NOT_GIVEN)
+    if wrapper_subclass is _NOT_GIVEN:
+        slotnames = copy_reg._slotnames(type_obj)
+        if slotnames and not isinstance(obj, _Wrapper):
+            new_type_dict = {'_Wrapper__DERIVED': True}
+            def _make_property(slotname):
+                 return property(lambda s: getattr(s._obj, slotname),
+                                 lambda s, v: setattr(s._obj, slotname, v),
+                                 lambda s: delattr(s._obj, slotname))
+            for slotname in slotnames:
+                new_type_dict[slotname] = _make_property(slotname)
+            new_type = type(cls.__name__ + '_' + type_obj.__name__,
+                            (cls,),
+                            new_type_dict)
+        else:
+            new_type = None
+        wrapper_subclass = _wrapper_subclass_cache[type_obj] = new_type
 
+    return wrapper_subclass
 
 class _Wrapper(ExtensionClass.Base):
-    __slots__ = ('_obj','_container',)
+    __slots__ = ('_obj','_container', '__dict__')
     _IS_IMPLICIT = None
 
     def __new__(cls, obj, container):
-        inst = super(_Wrapper,cls).__new__(cls)
+        wrapper_subclass = _make_wrapper_subclass_if_needed(cls, obj, container)
+        if wrapper_subclass:
+            inst = wrapper_subclass(obj, container)
+        else:
+            inst = super(_Wrapper,cls).__new__(cls)
         inst._obj = obj
         inst._container = container
+        if hasattr(obj, '__dict__') and not isinstance(obj, _Wrapper):
+            # Make our __dict__ refer to the same dict
+            # as the other object, so that if it has methods that
+            # use `object.__getattribute__` they still work. Note that because we have
+            # slots, we won't interfere with the contents of that dict
+            object.__setattr__(inst, '__dict__', obj.__dict__)
         return inst
 
     def __init__(self, obj, container):
@@ -325,11 +382,11 @@ class _Wrapper(ExtensionClass.Base):
 
     def __getattribute__(self, name):
         if name in ('_obj', '_container'):
-            return object.__getattribute__(self, name)
-        if self._obj is not None or self._container is not None:
+            return _OGA(self, name)
+        if _OGA(self, '_obj') is not None or _OGA(self, '_container') is not None:
             return _Wrapper_findattr(self, name, None, None, None,
                                      True, type(self)._IS_IMPLICIT, False, False)
-        return object.__getattribute__(self, name)
+        return _OGA(self, name)
 
     def __of__(self, parent):
         # Based on __of__ in the C code;
@@ -684,11 +741,15 @@ class _Acquirer(ExtensionClass.Base):
 
     def __getattribute__(self, name):
         try:
-            return ExtensionClass.Base.__getattribute__(self, name)
+            return super(_Acquirer, self).__getattribute__(name)
         except AttributeError:
             # the doctests have very specific error message
-            # requirements
-            raise AttributeError(name)
+            # requirements (but at least we can preserve the traceback)
+            _, _, tb = sys.exc_info()
+            try:
+                _reraise(AttributeError, AttributeError(name), tb)
+            finally:
+                del tb
 
     def __of__(self, context):
         return type(self)._Wrapper(self, context)

Pure-Python Wrapper objects break `object.__getattribute__` in methods of the wrapped object
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:

```python
    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.
diff --git a/src/Acquisition/tests.py b/src/Acquisition/tests.py
index 4bc523f..277d0fe 100644
--- a/src/Acquisition/tests.py
+++ b/src/Acquisition/tests.py
@@ -3124,6 +3124,125 @@ class TestAcquire(unittest.TestCase):
         found = self.acquire(self.a.b.c, AQ_PARENT)
         self.assertTrue(found.aq_self is self.a.b.aq_self)
 
+class TestCooperativeBase(unittest.TestCase):
+
+    def _make_acquirer(self, kind):
+        from ExtensionClass import Base
+
+        class ExtendsBase(Base):
+            def __getattribute__(self, name):
+                if name == 'magic':
+                    return 42
+                return super(ExtendsBase,self).__getattribute__(name)
+
+        class Acquirer(kind, ExtendsBase):
+            pass
+
+        return Acquirer()
+
+    def _check___getattribute___is_cooperative(self, acquirer):
+        self.assertEqual(getattr(acquirer, 'magic'), 42)
+
+    def test_implicit___getattribute__is_cooperative(self):
+        self._check___getattribute___is_cooperative(self._make_acquirer(Acquisition.Implicit))
+
+    def test_explicit___getattribute__is_cooperative(self):
+        self._check___getattribute___is_cooperative(self._make_acquirer(Acquisition.Explicit))
+
+if 'Acquisition._Acquisition' not in sys.modules:
+    # Implicitly wrapping an object that uses object.__getattribute__
+    # in its implementation of __getattribute__ doesn't break.
+    # This can arise with the `persistent` library or other
+    # "base" classes.
+
+    # The C implementation doesn't directly support this; however,
+    # it is used heavily in the Python implementation of Persistent.
+
+    class TestImplicitWrappingGetattribute(unittest.TestCase):
+
+        def test_object_getattribute_in_rebound_method_with_slots(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())
+
+            # Changing it is not reflected in the wrapper's dict (this is an
+            # implementation detail)
+            wrapper._Persistent__flags = -1
+            self.assertEqual(wrapped.get_flags(), -1)
+            self.assertEqual(wrapped.get_flags(), wrapper.get_flags())
+
+            wrapper_dict = object.__getattribute__(wrapper, '__dict__')
+            self.assertFalse('_Persistent__flags' in wrapper_dict)
+
+        def test_type_with_slots_reused(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)
+            wrapper2 = Acquisition.ImplicitAcquisitionWrapper(wrapped, None)
+
+            self.assertTrue( type(wrapper) is type(wrapper2))
+
+        def test_object_getattribute_in_rebound_method_with_dict(self):
+
+            class Persistent(object):
+                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())
+
+            # Changing it is also reflected in both dicts (this is an
+            # implementation detail)
+            wrapper._Persistent__flags = -1
+            self.assertEqual(wrapped.get_flags(), -1)
+            self.assertEqual(wrapped.get_flags(), wrapper.get_flags())
+
+            wrapper_dict = object.__getattribute__(wrapper, '__dict__')
+            self.assertTrue('_Persistent__flags' in wrapper_dict)
+
+
+        def test_object_getattribute_in_rebound_method_with_slots_and_dict(self):
+
+            class Persistent(object):
+                __slots__ = ('__flags', '__dict__')
+                def __init__(self):
+                    self.__flags = 42
+                    self.__oid = 'oid'
+
+                def get_flags(self):
+                    return object.__getattribute__(self, '_Persistent__flags')
+
+                def get_oid(self):
+                    return object.__getattribute__(self, '_Persistent__oid')
+
+            wrapped = Persistent()
+            wrapper = Acquisition.ImplicitAcquisitionWrapper(wrapped, None)
+
+            self.assertEqual(wrapped.get_flags(), wrapper.get_flags())
+            self.assertEqual(wrapped.get_oid(), wrapper.get_oid())
 
 class TestUnicode(unittest.TestCase):
 
@@ -3537,6 +3656,7 @@ def test_suite():
         unittest.makeSuite(TestAcquire),
         unittest.makeSuite(TestUnicode),
         unittest.makeSuite(TestProxying),
+        unittest.makeSuite(TestCooperativeBase),
     ]
 
     # This file is only available in a source checkout, skip it

pip install -e .
parse_log_pytest
requirements.txt
nose pytest
apt-get update apt-get install -y gcc
3.7
requirements/base.txt
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
-e git+https://github.com/zopefoundation/Acquisition.git@6f20ea96fff40814882da580844949ea97f6c4ba#egg=Acquisition
certifi @ file:///croot/certifi_1671487769961/work/certifi
exceptiongroup==1.2.2
ExtensionClass==5.1
importlib-metadata==6.7.0
iniconfig==2.0.0
nose==1.3.7
packaging==24.0
pluggy==1.2.0
pytest==7.4.4
tomli==2.0.1
typing_extensions==4.7.1
zipp==3.15.0
zope.interface==6.4.post2

name: Acquisition
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2022.12.7=py37h06a4308_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - pip=22.3.1=py37h06a4308_0
  - python=3.7.16=h7a1cb2a_0
  - readline=8.2=h5eee18b_0
  - setuptools=65.6.3=py37h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - wheel=0.38.4=py37h06a4308_0
  - xz=5.6.4=h5eee18b_1
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - exceptiongroup==1.2.2
      - extensionclass==5.1
      - importlib-metadata==6.7.0
      - iniconfig==2.0.0
      - nose==1.3.7
      - packaging==24.0
      - pluggy==1.2.0
      - pytest==7.4.4
      - tomli==2.0.1
      - typing-extensions==4.7.1
      - zipp==3.15.0
      - zope-interface==6.4.post2
prefix: /opt/conda/envs/Acquisition

src/Acquisition/tests.py::TestCooperativeBase::test_explicit___getattribute__is_cooperative src/Acquisition/tests.py::TestCooperativeBase::test_implicit___getattribute__is_cooperative src/Acquisition/tests.py::TestImplicitWrappingGetattribute::test_object_getattribute_in_rebound_method_with_dict src/Acquisition/tests.py::TestImplicitWrappingGetattribute::test_object_getattribute_in_rebound_method_with_slots src/Acquisition/tests.py::TestImplicitWrappingGetattribute::test_object_getattribute_in_rebound_method_with_slots_and_dict
src/Acquisition/tests.py::test_suite
src/Acquisition/tests.py::test_unwrapped src/Acquisition/tests.py::test_simple src/Acquisition/tests.py::test__of__exception src/Acquisition/tests.py::test_muliple src/Acquisition/tests.py::test_pinball src/Acquisition/tests.py::test_explicit src/Acquisition/tests.py::test_mixed_explicit_and_explicit src/Acquisition/tests.py::test_aq_inContextOf src/Acquisition/tests.py::test_AqAlg src/Acquisition/tests.py::test_explicit_acquisition src/Acquisition/tests.py::test_creating_wrappers_directly src/Acquisition/tests.py::test_cant_pickle_acquisition_wrappers_classic src/Acquisition/tests.py::test_cant_pickle_acquisition_wrappers_newstyle src/Acquisition/tests.py::test_cant_persist_acquisition_wrappers_classic src/Acquisition/tests.py::test_cant_persist_acquisition_wrappers_newstyle src/Acquisition/tests.py::test_interfaces src/Acquisition/tests.py::test_mixin_post_class_definition src/Acquisition/tests.py::test_mixin_base src/Acquisition/tests.py::test_Basic_gc src/Acquisition/tests.py::test_Wrapper_gc src/Acquisition/tests.py::test_container_proxying src/Acquisition/tests.py::test___parent__no_wrappers src/Acquisition/tests.py::test_implicit_wrapper_as___parent__ src/Acquisition/tests.py::test_explicit_wrapper_as___parent__ src/Acquisition/tests.py::test_implicit_wrapper_has_nonwrapper_as_aq_parent src/Acquisition/tests.py::test_explicit_wrapper_has_nonwrapper_as_aq_parent src/Acquisition/tests.py::test___parent__aq_parent_circles src/Acquisition/tests.py::test_python_impl_cycle src/Acquisition/tests.py::test_unwrapped_implicit_acquirer_unwraps__parent__ src/Acquisition/tests.py::test__iter__after_AttributeError src/Acquisition/tests.py::test_special_names src/Acquisition/tests.py::test_deleting_parent_attrs src/Acquisition/tests.py::test__cmp__is_called_on_wrapped_object src/Acquisition/tests.py::test_wrapped_methods_have_correct_self src/Acquisition/tests.py::test_cannot_set_attributes_on_empty_wrappers src/Acquisition/tests.py::test_getitem_setitem_not_implemented src/Acquisition/tests.py::test_getitem_setitem_implemented src/Acquisition/tests.py::test_wrapped_objects_are_unwrapped_on_set src/Acquisition/tests.py::test_wrapper_calls_of_on_non_wrapper src/Acquisition/tests.py::test_aq_inContextOf_odd_cases src/Acquisition/tests.py::test_search_repeated_objects src/Acquisition/tests.py::TestParent::test_parent_parent_circles src/Acquisition/tests.py::TestParent::test_parent_parent_parent_circles src/Acquisition/tests.py::TestAcquire::test_explicit_module_default src/Acquisition/tests.py::TestAcquire::test_explicit_module_false src/Acquisition/tests.py::TestAcquire::test_explicit_module_true src/Acquisition/tests.py::TestAcquire::test_explicit_wrapper_default src/Acquisition/tests.py::TestAcquire::test_explicit_wrapper_false src/Acquisition/tests.py::TestAcquire::test_explicit_wrapper_true src/Acquisition/tests.py::TestAcquire::test_no_wrapper_but___parent___falls_back_to_default src/Acquisition/tests.py::TestAcquire::test_unwrapped_falls_back_to_default src/Acquisition/tests.py::TestAcquire::test_w_unicode_attr_name src/Acquisition/tests.py::TestAcquire::test_wrapper_falls_back_to_default src/Acquisition/tests.py::TestImplicitWrappingGetattribute::test_type_with_slots_reused src/Acquisition/tests.py::TestUnicode::test_explicit_aq_unicode_should_be_called src/Acquisition/tests.py::TestUnicode::test_explicit_should_fall_back_to_str src/Acquisition/tests.py::TestUnicode::test_implicit_aq_unicode_should_be_called src/Acquisition/tests.py::TestUnicode::test_implicit_should_fall_back_to_str src/Acquisition/tests.py::TestUnicode::test_str_fallback_should_be_called_with_wrapped_self src/Acquisition/tests.py::TestUnicode::test_unicode_should_be_called_with_wrapped_self src/Acquisition/tests.py::TestProxying::test_explicit_proxy_bool src/Acquisition/tests.py::TestProxying::test_explicit_proxy_call src/Acquisition/tests.py::TestProxying::test_explicit_proxy_comporison src/Acquisition/tests.py::TestProxying::test_explicit_proxy_contains src/Acquisition/tests.py::TestProxying::test_explicit_proxy_hash src/Acquisition/tests.py::TestProxying::test_explicit_proxy_special_meths src/Acquisition/tests.py::TestProxying::test_implicit_proxy_bool src/Acquisition/tests.py::TestProxying::test_implicit_proxy_call src/Acquisition/tests.py::TestProxying::test_implicit_proxy_comporison src/Acquisition/tests.py::TestProxying::test_implicit_proxy_contains src/Acquisition/tests.py::TestProxying::test_implicit_proxy_hash src/Acquisition/tests.py::TestProxying::test_implicit_proxy_special_meths
src/Acquisition/__init__.py CHANGES.rst
src/Acquisition/__init__.py CHANGES.rst