|
| 1 | +from collections.abc import Iterator |
| 2 | +import contextlib |
1 | 3 | import importlib |
2 | 4 | import io |
3 | 5 | import itertools |
|
13 | 15 | from pkgutil import ModuleInfo |
14 | 16 | from unittest import TestCase, skipUnless, skipIf, SkipTest |
15 | 17 | from unittest.mock import Mock, patch |
16 | | -from test.support import force_not_colorized, make_clean_env, Py_DEBUG |
| 18 | +import warnings |
| 19 | +from test.support import ( |
| 20 | + captured_stdout, |
| 21 | + captured_stderr, |
| 22 | + force_not_colorized, |
| 23 | + make_clean_env, |
| 24 | + Py_DEBUG, |
| 25 | +) |
17 | 26 | from test.support import has_subprocess_support, SHORT_TIMEOUT, STDLIB_DIR |
18 | 27 | from test.support.import_helper import import_module |
19 | 28 | from test.support.os_helper import EnvironmentVarGuard, unlink |
@@ -1605,6 +1614,50 @@ def test_suggestions_and_messages(self) -> None: |
1605 | 1614 | new_imports = sys.modules.keys() - _imported |
1606 | 1615 | self.assertSetEqual(new_imports, expected_imports) |
1607 | 1616 |
|
| 1617 | +class TestModuleCompleterAutomaticImports(TestCase): |
| 1618 | + """Out of TestPyReplModuleCompleter case because it blocks module import.""" |
| 1619 | + |
| 1620 | + @classmethod |
| 1621 | + def setUpClass(cls) -> None: |
| 1622 | + super().setUpClass() |
| 1623 | + cls._audit_events: set[str] | None = None |
| 1624 | + def _hook(name: str, _args: tuple): |
| 1625 | + if cls._audit_events is not None: |
| 1626 | + cls._audit_events.add(name) |
| 1627 | + sys.addaudithook(_hook) |
| 1628 | + |
| 1629 | + @classmethod |
| 1630 | + @contextlib.contextmanager |
| 1631 | + def _capture_audit_events(cls) -> Iterator[set[str]]: |
| 1632 | + cls._audit_events = set() |
| 1633 | + try: |
| 1634 | + yield cls._audit_events |
| 1635 | + finally: |
| 1636 | + cls._audit_events = None |
| 1637 | + |
| 1638 | + def test_no_side_effects(self): |
| 1639 | + from test.test___all__ import AllTest # TODO: extract to a helper? |
| 1640 | + |
| 1641 | + completer = ModuleCompleter() |
| 1642 | + for _, modname in AllTest().walk_modules(completer._stdlib_path, ""): |
| 1643 | + with self.subTest(modname=modname): |
| 1644 | + with (captured_stdout() as out, |
| 1645 | + captured_stderr() as err, |
| 1646 | + self._capture_audit_events() as audit_events, |
| 1647 | + patch("tkinter._tkinter.create") as tk_mock, |
| 1648 | + warnings.catch_warnings(action="ignore"), |
| 1649 | + patch.dict(sys.modules)): |
| 1650 | + completer._maybe_import_module(modname) |
| 1651 | + # Test no module is imported that |
| 1652 | + # 1. prints any text |
| 1653 | + self.assertEqual(out.getvalue(), "") |
| 1654 | + self.assertEqual(err.getvalue(), "") |
| 1655 | + # 2. spawn any subprocess (eg. webbrowser.open) |
| 1656 | + self.assertNotIn("subprocess.Popen", audit_events) |
| 1657 | + # 3. launch a Tk window |
| 1658 | + tk_mock.assert_not_called() |
| 1659 | + |
| 1660 | + |
1608 | 1661 | class TestHardcodedSubmodules(TestCase): |
1609 | 1662 | @patch.dict(sys.modules) |
1610 | 1663 | def test_hardcoded_stdlib_submodules_are_importable(self): |
|
0 commit comments