Skip to content

Commit 6a5877f

Browse files
committed
Blacklist some stdlib modules from auto-import
1 parent 58e0e3d commit 6a5877f

File tree

2 files changed

+28
-9
lines changed

2 files changed

+28
-9
lines changed

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
@@ -157,15 +166,7 @@ def _find_attributes(self, path: str, prefix: str) -> tuple[list[str], Completio
157166
if not imported_module:
158167
if path in self._failed_imports: # Do not propose to import again
159168
return [], None
160-
root = path.split(".")[0]
161-
mod_info = next((m for m in self.global_cache if m.name == root),
162-
None)
163-
if mod_info and self._is_stdlib_module(mod_info):
164-
# Stdlib module: auto-import (no risk of dangerous side-effect)
165-
try:
166-
imported_module = importlib.import_module(path)
167-
except Exception:
168-
sys.modules.pop(path, None) # Clean half-imported module
169+
imported_module = self._maybe_import_module(path)
169170
if not imported_module:
170171
return [], self._get_import_completion_action(path)
171172
try:
@@ -246,6 +247,21 @@ def global_cache(self) -> list[pkgutil.ModuleInfo]:
246247
self._global_cache = list(pkgutil.iter_modules())
247248
return self._global_cache
248249

250+
def _maybe_import_module(self, fqname: str) -> ModuleType | None:
251+
if fqname in AUTO_IMPORT_BLACKLIST or fqname.endswith(".__main__"):
252+
# Special-cased modules with known import side-effects
253+
return None
254+
root = fqname.split(".")[0]
255+
mod_info = next((m for m in self.global_cache if m.name == root), None)
256+
if not mod_info or not self._is_stdlib_module(mod_info):
257+
# Only import stdlib modules (no risk of import side-effects)
258+
return None
259+
try:
260+
return importlib.import_module(fqname)
261+
except Exception:
262+
sys.modules.pop(fqname, None) # Clean half-imported module
263+
return None
264+
249265
def _get_import_completion_action(self, path: str) -> CompletionAction:
250266
prompt = ("[ module not imported, press again to import it "
251267
"and propose attributes ]")

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1163,6 +1163,9 @@ def test_attribute_completion_module_on_demand(self):
11631163
("from pack import b\t\t\t\n", "from pack import ba", {"pack"}),
11641164
# stdlib modules are automatically imported
11651165
("from graphlib import T\t\n", "from graphlib import TopologicalSorter", {"graphlib"}),
1166+
# except those with known side-effects
1167+
("from antigravity import g\t\n", "from antigravity import g", set()),
1168+
("from unittest.__main__ import \t\n", "from unittest.__main__ import ", set()),
11661169
)
11671170
for code, expected, expected_imports in cases:
11681171
with self.subTest(code=code), patch.dict(sys.modules):

0 commit comments

Comments
 (0)