Skip to content

Commit 58e0e3d

Browse files
committed
Auto-import stdlib modules
1 parent 8fd78ca commit 58e0e3d

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

Lib/_pyrepl/_module_completer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,16 @@ def _find_attributes(self, path: str, prefix: str) -> tuple[list[str], Completio
157157
if not imported_module:
158158
if path in self._failed_imports: # Do not propose to import again
159159
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+
if not imported_module:
160170
return [], self._get_import_completion_action(path)
161171
try:
162172
module_attributes = dir(imported_module)

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,6 +1140,7 @@ def test_attribute_completion_module_on_demand(self):
11401140
(dir / "pack" / "foo.py").touch()
11411141
(dir / "pack" / "bar.py").touch()
11421142
(dir / "pack" / "baz.py").touch()
1143+
sys.modules.pop("graphlib", None) # test modules may have been imported by previous tests
11431144
with patch.object(sys, "path", [_dir, *sys.path]):
11441145
cases = (
11451146
# needs 2 tabs to import (show prompt, then import)
@@ -1160,6 +1161,8 @@ def test_attribute_completion_module_on_demand(self):
11601161
("from pack import b\t\n", "from pack import ba", set()),
11611162
("from pack import b\t\t\n", "from pack import ba", set()),
11621163
("from pack import b\t\t\t\n", "from pack import ba", {"pack"}),
1164+
# stdlib modules are automatically imported
1165+
("from graphlib import T\t\n", "from graphlib import TopologicalSorter", {"graphlib"}),
11631166
)
11641167
for code, expected, expected_imports in cases:
11651168
with self.subTest(code=code), patch.dict(sys.modules):
@@ -1370,6 +1373,8 @@ def test_suggestions_and_messages(self) -> None:
13701373
(dir / "pack").mkdir()
13711374
(dir / "pack" / "__init__.py").write_text("foo = 1; bar = 2;")
13721375
(dir / "pack" / "bar.py").touch()
1376+
sys.modules.pop("graphlib", None) # test modules may have been imported by previous tests
1377+
sys.modules.pop("compression.zstd", None)
13731378
with patch.object(sys, "path", [_dir, *sys.path]):
13741379
cases = (
13751380
# no match != not an import
@@ -1391,6 +1396,9 @@ def test_suggestions_and_messages(self) -> None:
13911396
("from pack import ", (["bar", "foo"], None), set()),
13921397
("from pack.bar import ", ([], (_prompt, None)), {"pack.bar"}),
13931398
("from pack.bar import ", ([], None), set()),
1399+
# stdlib = auto-imported
1400+
("from graphlib import T", (["TopologicalSorter"], None), {"graphlib"}),
1401+
("from compression.zstd import c", (["compress"], None), {"compression.zstd"}),
13941402
)
13951403
completer = ModuleCompleter()
13961404
for i, (code, expected, expected_imports) in enumerate(cases):
@@ -1410,8 +1418,6 @@ def test_suggestions_and_messages(self) -> None:
14101418

14111419
new_imports = sys.modules.keys() - _imported
14121420
self.assertSetEqual(new_imports, expected_imports)
1413-
for mod in new_imports:
1414-
self.addCleanup(sys.modules.pop, mod)
14151421

14161422
class TestHardcodedSubmodules(TestCase):
14171423
@patch.dict(sys.modules)

0 commit comments

Comments
 (0)