Skip to content

Commit 78e0ce6

Browse files
loic-simonpablogsal
authored andcommitted
Auto-import stdlib modules
1 parent 42e734c commit 78e0ce6

2 files changed

Lines changed: 18 additions & 2 deletions

File tree

Lib/_pyrepl/_module_completer.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,16 @@ def _find_attributes(self, path: str, prefix: str) -> tuple[list[str], Completio
169169
if not imported_module:
170170
if path in self._failed_imports: # Do not propose to import again
171171
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+
if not imported_module:
172182
return [], self._get_import_completion_action(path)
173183
try:
174184
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
@@ -1324,6 +1324,7 @@ def test_attribute_completion_module_on_demand(self):
13241324
(dir / "pack" / "foo.py").touch()
13251325
(dir / "pack" / "bar.py").touch()
13261326
(dir / "pack" / "baz.py").touch()
1327+
sys.modules.pop("graphlib", None) # test modules may have been imported by previous tests
13271328
with patch.object(sys, "path", [_dir, *sys.path]):
13281329
cases = (
13291330
# needs 2 tabs to import (show prompt, then import)
@@ -1344,6 +1345,8 @@ def test_attribute_completion_module_on_demand(self):
13441345
("from pack import b\t\n", "from pack import ba", set()),
13451346
("from pack import b\t\t\n", "from pack import ba", set()),
13461347
("from pack import b\t\t\t\n", "from pack import ba", {"pack"}),
1348+
# stdlib modules are automatically imported
1349+
("from graphlib import T\t\n", "from graphlib import TopologicalSorter", {"graphlib"}),
13471350
)
13481351
for code, expected, expected_imports in cases:
13491352
with self.subTest(code=code), patch.dict(sys.modules):
@@ -1554,6 +1557,8 @@ def test_suggestions_and_messages(self) -> None:
15541557
(dir / "pack").mkdir()
15551558
(dir / "pack" / "__init__.py").write_text("foo = 1; bar = 2;")
15561559
(dir / "pack" / "bar.py").touch()
1560+
sys.modules.pop("graphlib", None) # test modules may have been imported by previous tests
1561+
sys.modules.pop("compression.zstd", None)
15571562
with patch.object(sys, "path", [_dir, *sys.path]):
15581563
cases = (
15591564
# no match != not an import
@@ -1575,6 +1580,9 @@ def test_suggestions_and_messages(self) -> None:
15751580
("from pack import ", (["bar", "foo"], None), set()),
15761581
("from pack.bar import ", ([], (_prompt, None)), {"pack.bar"}),
15771582
("from pack.bar import ", ([], None), set()),
1583+
# stdlib = auto-imported
1584+
("from graphlib import T", (["TopologicalSorter"], None), {"graphlib"}),
1585+
("from compression.zstd import c", (["compress"], None), {"compression.zstd"}),
15781586
)
15791587
completer = ModuleCompleter()
15801588
for i, (code, expected, expected_imports) in enumerate(cases):
@@ -1594,8 +1602,6 @@ def test_suggestions_and_messages(self) -> None:
15941602

15951603
new_imports = sys.modules.keys() - _imported
15961604
self.assertSetEqual(new_imports, expected_imports)
1597-
for mod in new_imports:
1598-
self.addCleanup(sys.modules.pop, mod)
15991605

16001606
class TestHardcodedSubmodules(TestCase):
16011607
@patch.dict(sys.modules)

0 commit comments

Comments
 (0)