From f03f1d55141b7e55a0265a545f9bbf051bd98979 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Thu, 1 Oct 2026 22:39:05 -0400 Subject: [PATCH 1/2] test(vendor): re-resolve VendorRuntimeWarning fresh per generation (#985) tests/vendor/test_vendor_snapshot_restore_unit.py bound VendorRuntimeWarning at module level, captured at import/collection time. Under plain unittest over tests/vendor (no conftest, no restore_module_table fixture), a sibling module purges pcapkit mid-run and re-imports it, minting a new generation of every pcapkit class -- the same skew #981 pins. The crawler under test then raises a different-generation VendorRuntimeWarning than the one assertWarnsRegex was still holding, so the assertion reports "not triggered" even though the warning is visibly on stderr one line earlier. - Drop the module-level `from pcapkit.utilities.warnings import VendorRuntimeWarning`. - Resolve it fresh in setUp() via importlib.import_module, alongside the vendor_main/Vendor pair that setUp already re-resolves per-test -- the same pattern test_base_class_contract.py's RegistrationGateTests.setUp() uses for #981. - Use self.VendorRuntimeWarning at the one call site. Red: `python -m unittest discover -s tests/vendor` (plain unittest, whole directory) failed with `AssertionError: VendorRuntimeWarning not triggered` on test_a_failure_leaves_the_previous_file_byte_for_byte_intact. Green: same command, Ran 118 tests in ~83s, OK. The module alone still passes: Ran 4 tests in 0.774s, OK. --- .../test_vendor_snapshot_restore_unit.py | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/vendor/test_vendor_snapshot_restore_unit.py b/tests/vendor/test_vendor_snapshot_restore_unit.py index 99cbec746b..5ae30896e8 100644 --- a/tests/vendor/test_vendor_snapshot_restore_unit.py +++ b/tests/vendor/test_vendor_snapshot_restore_unit.py @@ -173,8 +173,6 @@ import unittest from unittest import mock -from pcapkit.utilities.warnings import VendorRuntimeWarning - #: Same two-dependency gate the rest of :file:`tests/vendor/` uses. Importing #: :mod:`pcapkit.vendor.__main__` imports :mod:`pcapkit.vendor` (for its #: ``vendor_module`` default-target-list fallback), which pulls in every @@ -194,11 +192,40 @@ class SnapshotRestoreTests(unittest.TestCase): """``run()``'s snapshot-and-restore, isolated from fetch/render/discovery.""" def setUp(self) -> None: + """Re-resolve ``vendor_main``, ``Vendor`` and ``VendorRuntimeWarning``, fresh. + + GitHub issue #985, the same generation skew #981 pins in + :class:`tests.test_base_class_contract.RegistrationGateTests`: a sibling + module under :file:`tests/vendor/` (several call + :func:`tests._support.purge_modules` on ``pcapkit``, e.g. + :mod:`tests.vendor.test_vendor_dest_path_unit`) mints a fresh generation + of every :mod:`pcapkit` class, and :func:`tests.conftest.restore_module_table` + only reconciles that back under :program:`pytest` -- plain :mod:`unittest` + loads no ``conftest`` at all. A module-level ``from pcapkit.utilities.warnings + import VendorRuntimeWarning`` would bind whatever generation was live when + *this module* was first imported, which under ``python -m unittest + discover`` is before any sibling has purged anything; the crawler under + test, instantiated through ``self.vendor_main``/``self.Vendor`` below, + raises whatever generation is current *when the test runs*. The two can + disagree, and :meth:`unittest.TestCase.assertWarnsRegex` matches by class + identity, not by name -- so a stale module-level binding fails with + "VendorRuntimeWarning not triggered" even though the warning was actually + raised, one line earlier, on stderr. + + ``vendor_main`` and ``Vendor`` were already immune to this, because they + were resolved here in ``setUp`` rather than at module level. + ``VendorRuntimeWarning`` was not; it joins them, resolved through + :func:`importlib.import_module` so this always compares against the + same generation the crawler actually raises. + + """ import pcapkit.vendor.__main__ as vendor_main from pcapkit.vendor.default import Vendor self.vendor_main = vendor_main self.Vendor = Vendor + self.VendorRuntimeWarning = importlib.import_module( + 'pcapkit.utilities.warnings').VendorRuntimeWarning self._tempdir = tempfile.TemporaryDirectory(prefix='pcapkit-vendor-snapshot-restore-test-') self.addCleanup(self._tempdir.cleanup) @@ -283,7 +310,7 @@ def _raise_on_render(*args: object, **kwargs: object) -> None: # RuntimeError. A bare falsy check cannot tell "failed for the right # reason" from "failed for the wrong one"; this can. with mock.patch('builtins.print', side_effect=_raise_on_render): - with self.assertWarnsRegex(VendorRuntimeWarning, 'simulated failure'): + with self.assertWarnsRegex(self.VendorRuntimeWarning, 'simulated failure'): result = self.vendor_main.run(stub_crawler) self.assertFalse(result) From d47a6dcb979f396cea88963516a6db277ab2fc45 Mon Sep 17 00:00:00 2001 From: Jarry Shaw Date: Thu, 1 Oct 2026 22:58:13 -0400 Subject: [PATCH 2/2] docs(tests): name isinstance, not class identity, in the setUp docstring (#985) The setUp docstring added for GitHub issue #985 explained the generation skew by saying assertWarnsRegex "matches by class identity, not by name". unittest's _AssertWarnsContext.__exit__ actually filters with `isinstance(w, self.expected)`, so subclass semantics apply and a genuine subclass of the expected class matches. The docstring's conclusion still holds, for the reason the rewrite now gives: re-importing pcapkit re-mints VendorRuntimeWarning *and* its BaseWarning base, so the two generations are mutually unrelated -- issubclass is false in both directions and their MROs first converge on the builtin UserWarning. isinstance against a stale binding therefore rejects an instance of the fresh generation. Both carry the same __module__ and __name__, which is why the failure message names exactly the class that was raised. Prose only -- no code, assertion or import changed. Measured on CPython 3.14.7: issubclass false both ways, a subclass of the live generation passes assertWarnsRegex, an instance of the other generation fails "VendorRuntimeWarning not triggered". `python -m unittest discover -s tests/vendor`: Ran 118 tests, OK. --- .../vendor/test_vendor_snapshot_restore_unit.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/tests/vendor/test_vendor_snapshot_restore_unit.py b/tests/vendor/test_vendor_snapshot_restore_unit.py index 5ae30896e8..60cfb15066 100644 --- a/tests/vendor/test_vendor_snapshot_restore_unit.py +++ b/tests/vendor/test_vendor_snapshot_restore_unit.py @@ -207,10 +207,20 @@ def setUp(self) -> None: discover`` is before any sibling has purged anything; the crawler under test, instantiated through ``self.vendor_main``/``self.Vendor`` below, raises whatever generation is current *when the test runs*. The two can - disagree, and :meth:`unittest.TestCase.assertWarnsRegex` matches by class - identity, not by name -- so a stale module-level binding fails with + disagree, and :meth:`unittest.TestCase.assertWarnsRegex` tests each + captured warning with ``isinstance(warning_instance, expected_class)`` + against the class object it was handed -- not by name, and not by identity + either, so a genuine *subclass* of the expected class does match. That is + no rescue here, because the two generations are not related classes at + all: re-importing re-mints ``VendorRuntimeWarning`` *and* its + ``BaseWarning`` base, so ``issubclass`` is false in *both* directions and + the two MROs first converge on the builtin :exc:`UserWarning`. + ``isinstance`` against a stale binding therefore rejects an instance of + the fresh generation, and the assertion fails with "VendorRuntimeWarning not triggered" even though the warning was actually - raised, one line earlier, on stderr. + raised, one line earlier, on stderr -- both classes carry the same + ``__module__`` and ``__name__``, so the message names exactly the class + that *was* raised. ``vendor_main`` and ``Vendor`` were already immune to this, because they were resolved here in ``setUp`` rather than at module level.