Skip to content

chore: mark vermin's guarded-import false positives, close the engine handle - #411

Merged
JarryShaw merged 7 commits into
mainfrom
chore/hygiene-batch
Sep 16, 2026
Merged

JarryShaw merged 7 commits into
mainfrom
chore/hygiene-batch

Conversation

@JarryShaw

Copy link
Copy Markdown
Owner

Two small fixes and one measurement worth recording.

close_extractor leaked the engine handle

tests/_support.close_extractor closed _ifile only, while both Extractor._cleanup and Extractor.__exit__ close _ifile then _exeng.

That matters because Engine.close() is a no-op on the base class but not on the subclasses — pcap_ct and pypcap close a live pcap.pcap handle, pyshark closes a temp file. So an extractor abandoned mid-file in a test teardown leaked a real OS handle.

Now closes both, in the library's own order, each guarded separately and inside a try/finally so a failing stream close cannot strand the engine. getattr probing keeps it tolerant of an extractor that died before _exeng was assigned — which is the state teardown most often sees.

No test depended on the leak: all ~30 call sites use it purely as teardown and never touch the extractor afterwards. New tests/test_support_helpers.py pins the contract, following the tests/test_tier_guard.py precedent for covering test-support code.

# novermin on three guarded imports

Vermin reported a 3.11 minimum from enum.StrEnum and enum.show_flag_values, and 3.10 from typing.TypeAlias — all three in the else: branch of a sys.version_info guard, so they never execute on an older interpreter. False positives, and # novermin is Vermin's documented escape hatch for exactly this shape.

Verified the combined # type: ignore[attr-defined] # novermin still works: zero mypy errors in the file, and mypy does report unused ignores elsewhere in the same run, so the check has teeth.

targets = 3.6 left alone — and what that costs

Per your decision to keep the 3.6 claim. Recording the measurement so it doesn't have to be rediscovered:

After these three markers the report lands on 3.9, and what remains is real code, not false positives — 65 named-expression (walrus) sites across 57 files, unguarded and at module level, plus a positional-only / parameter in foundation/engines/engine.py. Walrus is 3.8+, so no amount of marking reaches 3.6.

Vermin isn't wired into any workflow, so nothing is red either way. Flagging it only because the gap between the declared floor and the actual one is now quantified.

Also measured, not changed

The packaging question, settled by building both artefacts: the sdist carries all 91 test modules, the wheel carries none. That is the right split — the suite could not run from an installed package anyway, since the generated captures aren't shipped and tests/_tiers.py resolves paths from a repository root. MANIFEST.in's prune test (singular) does not match tests, which is why the sdist includes them; changing it to prune tests would remove the suite from the sdist, the opposite of what was documented. Left as a deliberate decision, with the prose corrected on #408 instead.

Verification

tests/test_support_helpers.py + tests/utilities: 96 passed, 100 subtests.

… handle

Two small fixes and one measurement.

**`close_extractor` leaked the engine handle.** It closed `_ifile` only, while both
`Extractor._cleanup` and `Extractor.__exit__` close `_ifile` *then* `_exeng`. That
matters because `Engine.close()` is a no-op on the base class but not on the
subclasses: `pcap_ct` and `pypcap` close a live `pcap.pcap` handle and `pyshark`
closes a temp file, so an extractor abandoned mid-file in a test teardown leaked a
real OS handle. Now closes both, in the library's order, each guarded separately and
in a `try/finally` so a failing stream close cannot strand the engine. `getattr`
probing keeps it tolerant of an extractor that died before `_exeng` was assigned,
which is the state teardown most often sees.

No test depended on the leak: all ~30 call sites use it purely as teardown and never
touch the extractor afterwards. New `tests/test_support_helpers.py` pins the
contract, following the `tests/test_tier_guard.py` precedent for covering
test-support code.

**`# novermin` on three guarded imports in `compat.py`.** Vermin reported a 3.11
minimum from `enum.StrEnum` and `enum.show_flag_values`, and 3.10 from
`typing.TypeAlias`, all three in the `else:` branch of a `sys.version_info` guard --
so they never execute on an older interpreter and the report was a false positive.
`# novermin` is Vermin's documented escape hatch for exactly this. Verified the
combined `# type: ignore[attr-defined]  # novermin` still works: zero mypy errors in
the file, and mypy does report unused ignores elsewhere in the same run, so the check
has teeth.

`targets = 3.6` is left as it is, per the maintainer's decision to keep the 3.6
claim. Recording what that costs, since it was measured and should not have to be
re-discovered: after these three markers the report lands on **3.9**, and the
remaining floor is real code rather than false positives -- **65 named-expression
(walrus) sites across 57 files**, unguarded and at module level, plus a
positional-only `/` parameter in `foundation/engines/engine.py`. Walrus is 3.8+, so
no amount of marking reaches 3.6. Vermin is not wired into any workflow, so nothing
is red either way.

Verified: `tests/test_support_helpers.py` plus `tests/utilities` 96 passed, 100
subtests.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

_close_quietly can currently raise from getattr(target, "close", ...), which is a teardown-safety regression compared to the previous hasattr(...) behavior and should be guarded.

Get a fresh assessment by requesting another Copilot review.

Pull request overview

This PR tightens test teardown correctness by ensuring tests._support.close_extractor releases both the extractor’s input stream and its engine, and it reduces Vermin noise by marking version-guarded imports as false positives.

Changes:

  • Extend tests._support.close_extractor to close _ifile and _exeng in library order, with independent failure isolation.
  • Add a dedicated unit test module to pin teardown-helper behavior and prevent regressions.
  • Annotate pcapkit.utilities.compat guarded imports with # novermin to avoid static false positives.
File summaries
File Description
tests/test_support_helpers.py Adds contract tests covering teardown helper tolerance and close-order/independence.
tests/_support.py Updates teardown helper to close both stream and engine with guarding.
pcapkit/utilities/compat.py Marks guarded imports as Vermin false positives using # novermin.
Review details
  • Files reviewed: 3/3 changed files
  • Comments generated: 2
  • Review effort level: Lite

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread tests/_support.py Outdated
Comment thread tests/_support.py Outdated
…allowed

Addresses two review findings on #411:

- ``getattr(target, 'close', None)`` sat outside the ``try``, so a resource
  whose ``close`` is a property or resolved through ``__getattr__`` raised from
  the *lookup* and escaped -- the failure the helper exists to prevent, since it
  runs from teardown and would replace the real test failure. Measured against
  the previous code: it raised ``RuntimeError``. The lookup now happens inside
  the ``try``, and one unreachable resource still does not strand the other.
- The docstring said "swallowing anything it raises" of a bare
  ``except Exception``. It now says :exc:`Exception`, and says why
  :exc:`BaseException` is left to propagate.

Two tests added; 10 pass in the file, 659 in the fixture-free tier.
@JarryShaw
JarryShaw merged commit eada8f6 into main Sep 16, 2026
24 checks passed
@JarryShaw
JarryShaw deleted the chore/hygiene-batch branch September 17, 2026 01:06
@JarryShaw JarryShaw added ci Pull requests that change CI or workflow configuration (ci: subject prefix) chore Maintenance work: tooling, repo hygiene, no library behaviour change and removed ci Pull requests that change CI or workflow configuration (ci: subject prefix) labels Sep 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

chore Maintenance work: tooling, repo hygiene, no library behaviour change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants