Skip to content

Commit fbfe884

Browse files
committed
Do not propose hardcoded submodules if local import
1 parent a952c2a commit fbfe884

File tree

3 files changed

+29
-2
lines changed

3 files changed

+29
-2
lines changed

Lib/_pyrepl/_module_completer.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
from __future__ import annotations
22

3+
import importlib
4+
import os
35
import pkgutil
46
import sys
57
import token
68
import tokenize
9+
from importlib.machinery import FileFinder
710
from io import StringIO
811
from contextlib import contextmanager
912
from dataclasses import dataclass
@@ -50,6 +53,7 @@ def __init__(self, namespace: Mapping[str, Any] | None = None) -> None:
5053
self.namespace = namespace or {}
5154
self._global_cache: list[pkgutil.ModuleInfo] = []
5255
self._curr_sys_path: list[str] = sys.path[:]
56+
self._stdlib_path = os.path.dirname(importlib.__path__[0])
5357

5458
def get_completions(self, line: str) -> list[str] | None:
5559
"""Return the next possible import completions for 'line'."""
@@ -104,16 +108,27 @@ def _find_modules(self, path: str, prefix: str) -> list[str]:
104108
return []
105109

106110
modules: Iterable[pkgutil.ModuleInfo] = self.global_cache
111+
is_stdlib_import: bool | None = None
107112
for segment in path.split('.'):
108113
modules = [mod_info for mod_info in modules
109114
if mod_info.ispkg and mod_info.name == segment]
115+
if is_stdlib_import is None:
116+
# Top-level import decide if we import from stdlib or not
117+
is_stdlib_import = all(
118+
self._is_stdlib_module(mod_info) for mod_info in modules
119+
)
110120
modules = self.iter_submodules(modules)
111121

112122
module_names = [module.name for module in modules]
113-
module_names.extend(HARDCODED_SUBMODULES.get(path, ()))
123+
if is_stdlib_import:
124+
module_names.extend(HARDCODED_SUBMODULES.get(path, ()))
114125
return [module_name for module_name in module_names
115126
if self.is_suggestion_match(module_name, prefix)]
116127

128+
def _is_stdlib_module(self, module_info: pkgutil.ModuleInfo) -> bool:
129+
return (isinstance(module_info.module_finder, FileFinder)
130+
and module_info.module_finder.path == self._stdlib_path)
131+
117132
def is_suggestion_match(self, module_name: str, prefix: str) -> bool:
118133
if prefix:
119134
return module_name.startswith(prefix)

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,6 +1083,18 @@ def test_hardcoded_stdlib_submodules(self):
10831083
output = reader.readline()
10841084
self.assertEqual(output, expected)
10851085

1086+
def test_hardcoded_stdlib_submodules_not_proposed_if_local_import(self):
1087+
with tempfile.TemporaryDirectory() as _dir:
1088+
dir = pathlib.Path(_dir)
1089+
(dir / "collections").mkdir()
1090+
(dir / "collections" / "__init__.py").touch()
1091+
(dir / "collections" / "foo.py").touch()
1092+
with patch.object(sys, "path", [dir, *sys.path]):
1093+
events = code_to_events("import collections.\t\n")
1094+
reader = self.prepare_reader(events, namespace={})
1095+
output = reader.readline()
1096+
self.assertEqual(output, "import collections.foo")
1097+
10861098
def test_get_path_and_prefix(self):
10871099
cases = (
10881100
('', ('', '')),
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Fix some standard library sumbodules missing from the :term:`REPL` auto-completion of imports.
1+
Fix some standard library submodules missing from the :term:`REPL` auto-completion of imports.

0 commit comments

Comments
 (0)