diff --git a/tests/test_init_package.py b/tests/test_init_package.py index bf05f17b..9742601a 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.