tests/utilities/test_compat.py::test_python35_fallback_implementations fakes sys.version_info = (3, 5) and then executes pcapkit/utilities/compat.py:143, from aenum import StrEnum. On Python ≥ 3.11 that is the first-ever import aenum in the process, so aenum/_common.py:17 caches pyver = (3, 5) permanently. Every later use of aenum in that process is then operating under a false Python version.
The consequence is not subtle: a later import pcapkit dies at aenum/_enum.py:1640 with
AttributeError: 'TransportProtocol' object has no attribute '__set_name__'
because aenum skips the descriptor protocol it believes the running interpreter lacks.
Why it is normally invisible
Masked under pytest by tests/conftest.py's pytest_sessionstart, which imports pcapkit eagerly — so aenum is already imported with the true version before any test runs, and the fake assignment caches nothing new.
So this only bites when aenum has not already been imported: the stdlib unittest runner, a bare script, or any future change to that session hook. Confirmed with a standalone script using neither unittest nor mock.
Why the test cannot simply be left alone
The test is legitimate — it exercises the < 3.6 fallback branch of compat.py. The problem is that faking sys.version_info around a real import leaks state into a third-party module's import-time cache, and no amount of restoring sys.version_info afterwards undoes it, because the damage is a value already memoised in aenum._common.
Suggested directions, not prescribed
- Import
aenum before the fake so the cache is already warm with the true version — smallest change, but relies on ordering that nothing enforces.
- Run the fallback branch in a subprocess, so the poisoned cache dies with it. Most robust; costs a process per test.
- Reload
aenum._common afterwards — fragile, since other modules hold references to the cached value.
Whichever is chosen, a regression test should assert aenum._common.pyver matches the real interpreter after the suite, since that is the invariant being broken.
Provenance
Found by #686's worker while auditing sys.modules leakage for #674, and deliberately not fixed there — it is a different mechanism (a third-party import-time cache, not sys.modules) and tests/utilities/test_compat.py was outside that PR's scope. Reproduction is its measurement; I have not independently re-run it.
Related: #674 (the sys.modules restoration gap, fixed in #686) and the tests/cli/test_main.py leak filed alongside this.
tests/utilities/test_compat.py::test_python35_fallback_implementationsfakessys.version_info = (3, 5)and then executespcapkit/utilities/compat.py:143,from aenum import StrEnum. On Python ≥ 3.11 that is the first-everimport aenumin the process, soaenum/_common.py:17cachespyver = (3, 5)permanently. Every later use ofaenumin that process is then operating under a false Python version.The consequence is not subtle: a later
import pcapkitdies ataenum/_enum.py:1640withbecause
aenumskips the descriptor protocol it believes the running interpreter lacks.Why it is normally invisible
Masked under pytest by
tests/conftest.py'spytest_sessionstart, which importspcapkiteagerly — soaenumis already imported with the true version before any test runs, and the fake assignment caches nothing new.So this only bites when
aenumhas not already been imported: the stdlibunittestrunner, a bare script, or any future change to that session hook. Confirmed with a standalone script using neitherunittestnormock.Why the test cannot simply be left alone
The test is legitimate — it exercises the
< 3.6fallback branch ofcompat.py. The problem is that fakingsys.version_infoaround a realimportleaks state into a third-party module's import-time cache, and no amount of restoringsys.version_infoafterwards undoes it, because the damage is a value already memoised inaenum._common.Suggested directions, not prescribed
aenumbefore the fake so the cache is already warm with the true version — smallest change, but relies on ordering that nothing enforces.aenum._commonafterwards — fragile, since other modules hold references to the cached value.Whichever is chosen, a regression test should assert
aenum._common.pyvermatches the real interpreter after the suite, since that is the invariant being broken.Provenance
Found by #686's worker while auditing
sys.modulesleakage for #674, and deliberately not fixed there — it is a different mechanism (a third-party import-time cache, notsys.modules) andtests/utilities/test_compat.pywas outside that PR's scope. Reproduction is its measurement; I have not independently re-run it.Related: #674 (the
sys.modulesrestoration gap, fixed in #686) and thetests/cli/test_main.pyleak filed alongside this.