From 975ae2ac49b78cb2b35128562b7cf7bd95a24442 Mon Sep 17 00:00:00 2001 From: Vito Sansevero Date: Sat, 31 Oct 2026 15:42:29 -0700 Subject: [PATCH] test: reset a disposed fixture package's exit actions (#580) load_package imports the package under a test-only name; its exit actions stayed registered after cleanup_package removed it from sys.modules, so the suite ended with a ModuleNotFoundError from _kill_encoders_at_exit. cleanup_package now resets the package's exit signal. --- tests/test_init_package.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/tests/test_init_package.py b/tests/test_init_package.py index 42df3b58..6483e809 100644 --- a/tests/test_init_package.py +++ b/tests/test_init_package.py @@ -136,6 +136,11 @@ def cleanup_package(package, require_stopped=True): if registration is not None: assert registration.dispose(3), "package history worker leaked" close_every_database() + # The package's exit actions outlive it otherwise: at interpreter exit + # they would import from a package no longer in sys.modules (#580). + exit_signal = getattr(package, "_exit_signal", None) + if exit_signal is not None: + exit_signal.reset() sys.modules.pop(package.__name__, None) @@ -402,6 +407,29 @@ class TestConsoleBanner(unittest.TestCase): self.assertNotIn("Loaded:", banner) +class TestFixtureExitActions(unittest.TestCase): + """A disposed fixture package runs nothing at interpreter exit (#580).""" + + def test_a_disposed_package_leaves_no_exit_action(self): + tmp = tempfile.TemporaryDirectory() + self.addCleanup(tmp.cleanup) + self.addCleanup(close_every_database) + output_dir = os.path.join(tmp.name, "output") + os.makedirs(output_dir) + name = "prompt_manager_pkg_exit_actions" + self.addCleanup(sys.modules.pop, name, None) + + with contextlib.redirect_stdout(io.StringIO()): + with load_package(name, os.path.join(tmp.name, "p.db"), output_dir) as ( + package, + _, + ): + signal = package._exit_signal + self.assertTrue(signal._on_exit, "the package installs exit actions") + + self.assertEqual(signal._on_exit, []) + + class PriorFileHandler(logging.FileHandler): """A caller-owned sink that stays closed once its owner closes it.