Skip to content

Commit f02fc8f

Browse files
loic-simonpablogsal
authored andcommitted
Improve & test auto-import blacklist
1 parent 61e797a commit f02fc8f

2 files changed

Lines changed: 61 additions & 5 deletions

File tree

Lib/_pyrepl/_module_completer.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import importlib
44
import os
55
import pkgutil
6+
import re
67
import sys
78
import token
89
import tokenize
@@ -32,9 +33,11 @@
3233
AUTO_IMPORT_BLACKLIST = {
3334
# Standard library modules/submodules that have import side effects
3435
# and must not be automatically imported to complete attributes
35-
"antigravity",
36-
"this",
37-
"idlelib.idle",
36+
re.compile(r"antigravity"),
37+
re.compile(r"this"),
38+
re.compile(r"idlelib\..+"),
39+
re.compile(r"test\..+"),
40+
re.compile(r".+\.__main__"),
3841
}
3942

4043

@@ -259,7 +262,7 @@ def global_cache(self) -> list[pkgutil.ModuleInfo]:
259262
return self._global_cache
260263

261264
def _maybe_import_module(self, fqname: str) -> ModuleType | None:
262-
if fqname in AUTO_IMPORT_BLACKLIST or fqname.endswith(".__main__"):
265+
if any(pattern.fullmatch(fqname) for pattern in AUTO_IMPORT_BLACKLIST):
263266
# Special-cased modules with known import side-effects
264267
return None
265268
root = fqname.split(".")[0]

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from collections.abc import Iterator
2+
import contextlib
13
import importlib
24
import io
35
import itertools
@@ -13,7 +15,14 @@
1315
from pkgutil import ModuleInfo
1416
from unittest import TestCase, skipUnless, skipIf, SkipTest
1517
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+
)
1726
from test.support import has_subprocess_support, SHORT_TIMEOUT, STDLIB_DIR
1827
from test.support.import_helper import import_module
1928
from test.support.os_helper import EnvironmentVarGuard, unlink
@@ -1605,6 +1614,50 @@ def test_suggestions_and_messages(self) -> None:
16051614
new_imports = sys.modules.keys() - _imported
16061615
self.assertSetEqual(new_imports, expected_imports)
16071616

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+
16081661
class TestHardcodedSubmodules(TestCase):
16091662
@patch.dict(sys.modules)
16101663
def test_hardcoded_stdlib_submodules_are_importable(self):

0 commit comments

Comments
 (0)