Clear stdlib ABC caches in purge_modules (fixes flaky 3.10 suite) - #381
Conversation
purge_modules() drops pcapkit from sys.modules so the next test re-imports it
fresh, but the collections.abc ABCs are never purged. Every re-import rebuilds
pcapkit's Mapping subclasses (Info, Schema, ContextRegistry, ProtocolContext,
EnumSchema) as new class objects, and that churn corrupts the C-level
_abc_impl instance-check caches on the shared ABCs. Those caches then give
stale answers for immortal built-ins: isinstance({}, collections.abc.Mapping)
returns False, or isinstance({}, Schema) returns True, until the cache token
happens to advance.
The effect is order-dependent and self-heals by teardown, so every test file
passes in isolation while the full suite fails intermittently on Python 3.10 -
ContextRegistry.make() taking a dict down its Iterable branch and registering
the dict's keys, or Schema.to_dict() being called on a plain dict. The
maintainer had already worked around one instance of this at
pcapkit/corekit/infoclass.py:270 with a belt-and-braces isinstance check.
purge_modules now resets the ABC caches (abc._reset_caches, guarded so a future
runtime without it degrades rather than errors) after the purge, which fixes
the root rather than another symptom.
3.10 CI selection: 347 passed, 14 skipped, 156 subtests passed, 0 failed.
There was a problem hiding this comment.
🟡 Changes recommended
The new _reset_abc_caches() implementation calls abc._reset_caches with an argument, which is incompatible with CPython’s no-arg internal API and will raise TypeError during test setup.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
This PR addresses intermittent, order-dependent test failures on Python 3.10 by strengthening the test harness’s module-purge behavior to avoid stale collections.abc instance-check cache results across repeated pcapkit re-imports.
Changes:
- Add a helper to reset stdlib ABC caches after
pcapkitis purged fromsys.modules. - Invoke the ABC-cache reset from
purge_modulesto reduce cross-test contamination.
File summaries
| File | Description |
|---|---|
| tests/_support.py | Adds _reset_abc_caches() and calls it from purge_modules() to mitigate flaky failures caused by stale ABC instance-check caches across repeated re-imports. |
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 1
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
🟢 Approval recommended
The change is test-harness-only, uses guarded internal APIs with a safe fallback, and directly targets the documented Python 3.10 flakiness mechanism.
Review details
- Files reviewed: 1/1 changed files
- Comments generated: 0 new
- Review effort level: Lite
62b8303 to
d0def08
Compare
|
Reverted the
The original per-class form ( |
Fixes a latent test-harness bug that surfaces as intermittent, order-dependent failures on Python 3.10. It is what blocks CI on #378, but it is not an ESP bug — it predates that branch and affects the whole suite.
Symptom
On 3.10, the full suite intermittently fails one of:
test_context_registry_normalisation→RegistryError: not a protocol context: 'ESP'test_*_option_constructors_cover_*(ipv4/mh/ipv6) →AttributeError: 'dict' object has no attribute 'to_dict'Every one of these passes when its file runs alone. Which test fails depends on collection order.
Root cause
tests/_support.purge_modules(['pcapkit'])— called in almost everysetUp— drops pcapkit fromsys.modulesso the next test re-imports it fresh. But the stdlibcollections.abcABCs are never purged. Each re-import rebuilds pcapkit'sMappingsubclasses (Info,Schema,ContextRegistry,ProtocolContext,EnumSchema) as brand-new class objects, and that repeated subclass churn corrupts the C-level_abc_implinstance-check caches on the shared ABCs.The caches then return stale answers for immortal built-ins:
isinstance({}, collections.abc.Mapping)→ False, soContextRegistry.make({'ESP': ctx})skips its Mapping branch, iterates the dict's keys, and callsregister('ESP').isinstance({}, Schema)→ True, soSchema.to_dict()(schema.py:437) is called on a plain dict.It self-heals by teardown as the cache token advances, which is exactly why it is invisible per-file and only bites the full suite.
Confirmed pre-existing: the commit before the ESP const/vendor work fails identically, and
pcapkit/corekit/infoclass.py:270already carries a defensiveisinstance(dict_, (dict, collections.abc.Mapping))— a prior workaround for the same fragility in one spot.Fix
purge_modulesnow resets the ABC caches after the purge, viaabc._reset_cachesover thecollections.abcmembers. It is a CPython internal (present on the C_abcand pure-python_py_abcbackends), guarded withgetattrso a future runtime without it degrades to the old occasionally-flaky behaviour rather than erroring.gc.collect()was tried and does not work — the poison is in the ABC caches, not in dead subclasses.Verification (Python 3.10)
tests/protocols/internetalone on Implement ESP, with optional payload decryption #378's branch: 88 passed, 14 skipped, 0 failed (theto_dictcascade gone).#378 will be rebased onto this once it merges.