Skip to content

Commit 4af851b

Browse files
loic-simonpablogsal
authored andcommitted
Blacklist some stdlib modules from auto-import
1 parent 78e0ce6 commit 4af851b

2 files changed

Lines changed: 28 additions & 9 deletions

File tree

Lib/_pyrepl/_module_completer.py

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
TYPE_CHECKING = False
1717

1818
if TYPE_CHECKING:
19+
from types import ModuleType
1920
from typing import Any, Iterable, Iterator, Mapping
2021
from .types import CompletionAction
2122

@@ -28,6 +29,14 @@
2829
"xml.parsers.expat": ["errors", "model"],
2930
}
3031

32+
AUTO_IMPORT_BLACKLIST = {
33+
# Standard library modules/submodules that have import side effects
34+
# and must not be automatically imported to complete attributes
35+
"antigravity",
36+
"this",
37+
"idlelib.idle",
38+
}
39+
3140

3241
def make_default_module_completer() -> ModuleCompleter:
3342
# Inside pyrepl, __package__ is set to None by default
@@ -169,15 +178,7 @@ def _find_attributes(self, path: str, prefix: str) -> tuple[list[str], Completio
169178
if not imported_module:
170179
if path in self._failed_imports: # Do not propose to import again
171180
return [], None
172-
root = path.split(".")[0]
173-
mod_info = next((m for m in self.global_cache if m.name == root),
174-
None)
175-
if mod_info and self._is_stdlib_module(mod_info):
176-
# Stdlib module: auto-import (no risk of dangerous side-effect)
177-
try:
178-
imported_module = importlib.import_module(path)
179-
except Exception:
180-
sys.modules.pop(path, None) # Clean half-imported module
181+
imported_module = self._maybe_import_module(path)
181182
if not imported_module:
182183
return [], self._get_import_completion_action(path)
183184
try:
@@ -257,6 +258,21 @@ def global_cache(self) -> list[pkgutil.ModuleInfo]:
257258
self._global_cache = list(pkgutil.iter_modules())
258259
return self._global_cache
259260

261+
def _maybe_import_module(self, fqname: str) -> ModuleType | None:
262+
if fqname in AUTO_IMPORT_BLACKLIST or fqname.endswith(".__main__"):
263+
# Special-cased modules with known import side-effects
264+
return None
265+
root = fqname.split(".")[0]
266+
mod_info = next((m for m in self.global_cache if m.name == root), None)
267+
if not mod_info or not self._is_stdlib_module(mod_info):
268+
# Only import stdlib modules (no risk of import side-effects)
269+
return None
270+
try:
271+
return importlib.import_module(fqname)
272+
except Exception:
273+
sys.modules.pop(fqname, None) # Clean half-imported module
274+
return None
275+
260276
def _get_import_completion_action(self, path: str) -> CompletionAction:
261277
prompt = ("[ module not imported, press again to import it "
262278
"and propose attributes ]")

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,9 @@ def test_attribute_completion_module_on_demand(self):
13471347
("from pack import b\t\t\t\n", "from pack import ba", {"pack"}),
13481348
# stdlib modules are automatically imported
13491349
("from graphlib import T\t\n", "from graphlib import TopologicalSorter", {"graphlib"}),
1350+
# except those with known side-effects
1351+
("from antigravity import g\t\n", "from antigravity import g", set()),
1352+
("from unittest.__main__ import \t\n", "from unittest.__main__ import ", set()),
13501353
)
13511354
for code, expected, expected_imports in cases:
13521355
with self.subTest(code=code), patch.dict(sys.modules):

0 commit comments

Comments
 (0)