Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support PURE_PYTHON instead of AC_PURE_PYTHON #120

Merged
merged 11 commits into from Feb 16, 2022
Merged

Conversation

icemac
Copy link
Member

@icemac icemac commented Nov 29, 2021

I fixed the obvious problems and disabled the tests which explicitly test the C implementation so only the actually problematic ones remain.

Locally I get the following result for these changes:

$ tox -epy39
py39 installed: zc.buildout==2.13.6
py39 run-test-pre: PYTHONHASHSEED='2895537127'
py39 run-test: commands[0] | /.../AccessControl/.tox/py39/bin/buildout -nc /.../AccessControl/buildout.cfg buildout:directory=/.../AccessControl/.tox/py39 buildout:develop=/.../AccessControl install test
Develop: '/.../AccessControl'
Updating test.
Generated script '/.../AccessControl/.tox/py39/bin/test'.
py39 run-test: commands[1] | /.../AccessControl/.tox/py39/bin/test --all -pvc
Running tests at all levels
Running zope.testrunner.layer.UnitTests tests:
  Set up zope.testrunner.layer.UnitTests in 0.000 seconds.
  Running:
    106/290 (36.6%) testPython (...ntrol.tests.testZopeGuards.TestActualPython)

Error in test testPython (AccessControl.tests.testZopeGuards.TestActualPython)
Traceback (most recent call last):
  File "/.../lib/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/.../lib/python3.9/unittest/case.py", line 592, in run
    self._callTestMethod(testMethod)
  File "/.../lib/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/.../AccessControl/src/AccessControl/tests/testZopeGuards.py", line 756, in testPython
    exec(code, its_globals)
  File "/.../AccessControl/src/AccessControl/tests/actual_python.py", line 115, in <module>
    f6()
  File "/.../AccessControl/src/AccessControl/tests/actual_python.py", line 97, in f6
    assert getattr(C, "display", None) == getattr(C, "display")
  File "/.../AccessControl/src/AccessControl/tests/testZopeGuards.py", line 655, in __call__
    return self.func(*args, **kws)
  File "/.../AccessControl/src/AccessControl/ImplPython.py", line 767, in guarded_getattr
    aq_acquire(inst, name, aq_validate, validate)
  File "/.../eggs/cp39/Acquisition-4.8-py3.9-macosx-11.0-x86_64.egg/Acquisition/__init__.py", line 802, in aq_acquire
    return aq_acquire(ImplicitAcquisitionWrapper(obj, parent),
  File "/.../eggs/cp39/Acquisition-4.8-py3.9-macosx-11.0-x86_64.egg/Acquisition/__init__.py", line 389, in __new__
    object.__setattr__(inst, '__dict__', obj.__dict__)
TypeError: __dict__ must be set to a dictionary, not a 'mappingproxy'

    107/290 (36.9%) testPythonRealAC (...tests.testZopeGuards.TestActualPython)

Error in test testPythonRealAC (AccessControl.tests.testZopeGuards.TestActualPython)
Traceback (most recent call last):
  File "/.../lib/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/.../lib/python3.9/unittest/case.py", line 592, in run
    self._callTestMethod(testMethod)
  File "/.../lib/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/.../AccessControl/src/AccessControl/tests/testZopeGuards.py", line 769, in testPythonRealAC
    exec(code, its_globals)
  File "/.../AccessControl/src/AccessControl/tests/actual_python.py", line 115, in <module>
    f6()
  File "/.../AccessControl/src/AccessControl/tests/actual_python.py", line 97, in f6
    assert getattr(C, "display", None) == getattr(C, "display")
  File "/.../AccessControl/src/AccessControl/ImplPython.py", line 767, in guarded_getattr
    aq_acquire(inst, name, aq_validate, validate)
  File "/.../eggs/cp39/Acquisition-4.8-py3.9-macosx-11.0-x86_64.egg/Acquisition/__init__.py", line 802, in aq_acquire
    return aq_acquire(ImplicitAcquisitionWrapper(obj, parent),
  File "/.../eggs/cp39/Acquisition-4.8-py3.9-macosx-11.0-x86_64.egg/Acquisition/__init__.py", line 389, in __new__
    object.__setattr__(inst, '__dict__', obj.__dict__)
TypeError: __dict__ must be set to a dictionary, not a 'mappingproxy'

    121/290 (41.7%) testIdentityProxy (...stZopeSecurityPolicy.Python_ZSPTests)

Failure in test testIdentityProxy (AccessControl.tests.testZopeSecurityPolicy.Python_ZSPTests)
Traceback (most recent call last):
  File "/.../lib/python3.9/unittest/case.py", line 59, in testPartExecutor
    yield
  File "/.../lib/python3.9/unittest/case.py", line 592, in run
    self._callTestMethod(testMethod)
  File "/.../lib/python3.9/unittest/case.py", line 550, in _callTestMethod
    method()
  File "/.../AccessControl/src/AccessControl/tests/testZopeSecurityPolicy.py", line 249, in testIdentityProxy
    self.assertEqual(rc, sys.getrefcount(eo))
  File "/.../lib/python3.9/unittest/case.py", line 837, in assertEqual
    assertion_func(first, second, msg=msg)
  File "/.../lib/python3.9/unittest/case.py", line 830, in _baseAssertEqual
    raise self.failureException(msg)
AssertionError: 3 != 12


  Ran 290 tests with 1 failures, 2 errors, 21 skipped in 0.200 seconds.
Tearing down left over layers:
  Tear down zope.testrunner.layer.UnitTests in 0.000 seconds.

Tests with errors:
   testPython (AccessControl.tests.testZopeGuards.TestActualPython)
   testPythonRealAC (AccessControl.tests.testZopeGuards.TestActualPython)

Tests with failures:
   testIdentityProxy (AccessControl.tests.testZopeSecurityPolicy.Python_ZSPTests)
ERROR: InvocationError for command /.../AccessControl/.tox/py39/bin/test --all -pvc (exited with code 1)
_________________________________________________________________________ summary __________________________________________________________________________
ERROR:   py39: commands failed

See #119 for the base of this PR.

…PYTHON.

I disabled the tests which explicitly test the C implementation so only the
actually problematic ones remain.
@icemac icemac added the do not merge Not (yet) ready to be merged. label Nov 29, 2021
@icemac icemac requested a review from d-maurer November 29, 2021 07:26
@icemac
Copy link
Member Author

icemac commented Nov 29, 2021

Interestingly GHA fails in other interesting ways: https://github.com/zopefoundation/AccessControl/runs/4350600029?check_suite_focus=true

@d-maurer
Copy link
Contributor

d-maurer commented Nov 29, 2021 via email

@icemac
Copy link
Member Author

icemac commented Nov 30, 2021

In the "PURE_PYTHON" version, AccessControl should not import cAccessControl

I did this in 0005e9f – locally it did not change anything. The failures shown in the description of this PR stay the same.

@icemac
Copy link
Member Author

icemac commented Nov 30, 2021

https://github.com/zopefoundation/AccessControl/runs/4364181714?check_suite_focus=true now shows the same failures I see locally using tox.

@d-maurer
Copy link
Contributor

d-maurer commented Dec 1, 2021

TypeError: dict must be set to a dictionary, not a 'mappingproxy'

This is likely a limitation of the Python implementation for Acquisition: it does (currently) not allow to call aq_acquire on a class with a filter. For a class, __dict__ is not a regular dict but a dict proxy and this lets the ImplicitAcquisitionWrapper constructor fail.

@icemac
Copy link
Member Author

icemac commented Dec 2, 2021

@d-maurer Thank you for the explanation. What do you suggest as next steps?

@d-maurer
Copy link
Contributor

d-maurer commented Dec 2, 2021 via email

@d-maurer
Copy link
Contributor

d-maurer commented Dec 2, 2021

The failing refcount test looks strange -- as if something would create cycles. I will investigate once I can reproduce the problem in my typical environment (no tox).

Indeed: something seems to have created cycles involving frame objects:

(Pdb) !from gc import get_referrers
(Pdb) !gr = get_referrers
(Pdb) pp gr(eo)
[[<AccessControl.tests.testZopeSecurityPolicy.ImplictAcqObject object at 0x7f981dc28470>],
 <frame object at 0x7f981dc293e8>,
 <frame object at 0x178e9f8>,
 <frame object at 0x17f6378>,
 <frame object at 0x1826408>,
 <frame object at 0x1827138>,
 <frame object at 0x176f938>,
 <frame object at 0x1826e78>,
 <frame object at 0x182b908>,
 <frame object at 0x182b138>,
 <frame object at 0x182bc58>,
 {'eo': <AccessControl.tests.testZopeSecurityPolicy.ImplictAcqObject object at 0x7f981dc28470>,
  'get_referrers': <built-in function get_referrers>,
  'gr': <built-in function get_referrers>,
  'rc': 3,
  'self': <AccessControl.tests.testZopeSecurityPolicy.Python_ZSPTests testMethod=testIdentityProxy>}]
(Pdb) !import gc; gc.collect()
188
(Pdb) p sys.getrefcount(eo)
4
(Pdb) pp gr(eo)
[{'eo': <AccessControl.tests.testZopeSecurityPolicy.ImplictAcqObject object at 0x7f981dc28470>,
  'gc': <module 'gc' (built-in)>,
  'get_referrers': <built-in function get_referrers>,
  'gr': <built-in function get_referrers>,
  'rc': 3,
  'self': <AccessControl.tests.testZopeSecurityPolicy.Python_ZSPTests testMethod=testIdentityProxy>},
 [<AccessControl.tests.testZopeSecurityPolicy.ImplictAcqObject object at 0x7f981dc28470>],
 <frame object at 0x7f981dc293e8>]

I push a commit which calls gc.collect before refcount checks. This lets the test succeed but we might be interested to find out where the reference cycles come from.

@d-maurer
Copy link
Contributor

d-maurer commented Dec 4, 2021

I push a commit which calls gc.collect before refcount checks. This lets the test succeed but we might be interested to find out where the reference cycles come from.

I tried to locate the cycles -- but I failed. The likely reason is that pdb internally calls gc.collect; thus, when I use pdb to search for cycles, they get reclaimed and disappear.

When I remember right, then gc has an option which disables the release of cycles (instead they are placed into a gc structure for examination). With something like this, the cause of the cyclic structures might be found. But for the moment, I give up.

With the changes in this PR and zopefoundation/Acquisition#57 the tests should pass. I will merge the Acquisition PR on Monday (unless someone objects).

I suggest again, that we harmonize PURE_PYTHON and AC_PURE_PYTHON. Other packages use the logic: use the Python implementation for either Pypy or if the PURE_PYTHON envvar is set. For AccessControl we may add an additional "or if AC_PURE_PYTHON is set" to be able to check its Python implementation together with C extensions of other packages but it should behave similar to other packages regarding the PURE_PYTHON envvar.

@d-maurer
Copy link
Contributor

d-maurer commented Dec 6, 2021

I tried to locate the cycles -- but I failed.

I have found the cause of the reference cycles --> "zopefoundation/Acquisition#57 (comment)".

@icemac
Copy link
Member Author

icemac commented Dec 17, 2021

Now only Python 3.8 on Ubuntu fails reliably, see https://github.com/zopefoundation/AccessControl/runs/4557545726?check_suite_focus=true
Locally on MacOS this does not fail as on MacOS in CI.

@d-maurer Do you have any idea?

@icemac
Copy link
Member Author

icemac commented Feb 10, 2022

@d-maurer Do you have an idea about the Python 3.8 problem on Linux or are we forced to go with AC_PURE_PYTHON as implemented in #119?

@d-maurer
Copy link
Contributor

@d-maurer Do you have an idea about the Python 3.8 problem on Linux or are we forced to go with AC_PURE_PYTHON as implemented in #119?

The test failure indicates that there are fewer references than expected. The huge number of references (in the 30,000) indicates that the corresponding object is extremely popular -- and therefore quite unfit to be used in a reference count test. I assume that the test failure results from an intervening garbage collection (when garbage collection is activated can depend on small variations, e.g. the Python version).

In my view, the test is buggy. It should use a new object, not one referenced from thousands of places.

If we do not want to change the test object, we can call the garbage collector manually before the getrefcount calls, to become independent from automatic garbage collector activation.

@icemac
Copy link
Member Author

icemac commented Feb 11, 2022

@d-maurer Thank you for looking again into this issue. I saw that another test disables gc for the test run, so I do now the same for the test case which includes the failing test.

@icemac
Copy link
Member Author

icemac commented Feb 11, 2022

Tests were successfully running at https://github.com/zopefoundation/AccessControl/actions/runs/1828061585. (Had to start them manually as GH had disabled the workflow.)

So I think we can prepare to merge this PR.

.github/workflows/tests.yml Outdated Show resolved Hide resolved
@icemac icemac changed the title Example to show that AccessControl currently does not work with PURE_PYTHON. Example to show that AccessControl currently ~~does not~~ work with PURE_PYTHON. Feb 16, 2022
@icemac icemac changed the title Example to show that AccessControl currently ~~does not~~ work with PURE_PYTHON. Example to show that AccessControl currently ~does not~ work with PURE_PYTHON. Feb 16, 2022
@icemac icemac changed the title Example to show that AccessControl currently ~does not~ work with PURE_PYTHON. Example to show that AccessControl currently ~~does not~~ work with PURE_PYTHON. Feb 16, 2022
@icemac icemac changed the title Example to show that AccessControl currently ~~does not~~ work with PURE_PYTHON. Support PURE_PYTHON instead of AC_PURE_PYTHON Feb 16, 2022
@icemac icemac enabled auto-merge (squash) February 16, 2022 07:33
@icemac icemac merged commit aa99201 into pure-python-switch Feb 16, 2022
@icemac icemac removed the do not merge Not (yet) ready to be merged. label Feb 23, 2022
@icemac icemac deleted the pure-python branch February 24, 2022 07:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants