Skip to content

gh-69605: Disable PyREPL module autocomplete fallback on regular completion #134181

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 25, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions Lib/_pyrepl/_module_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,14 @@ def __init__(self, namespace: Mapping[str, Any] | None = None) -> None:
self._global_cache: list[pkgutil.ModuleInfo] = []
self._curr_sys_path: list[str] = sys.path[:]

def get_completions(self, line: str) -> list[str]:
"""Return the next possible import completions for 'line'."""
def get_completions(self, line: str) -> list[str] | None:
"""Return the next possible import completions for 'line'.

If 'line' is not an import statement, return None.
"""
result = ImportParser(line).parse()
if not result:
return []
return None
try:
return self.complete(*result)
except Exception:
Expand Down
5 changes: 3 additions & 2 deletions Lib/_pyrepl/readline.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,8 @@ def get_stem(self) -> str:
return "".join(b[p + 1 : self.pos])

def get_completions(self, stem: str) -> list[str]:
if module_completions := self.get_module_completions():
module_completions = self.get_module_completions()
if module_completions is not None:
return module_completions
if len(stem) == 0 and self.more_lines is not None:
b = self.buffer
Expand Down Expand Up @@ -165,7 +166,7 @@ def get_completions(self, stem: str) -> list[str]:
result.sort()
return result

def get_module_completions(self) -> list[str]:
def get_module_completions(self) -> list[str] | None:
line = self.get_line()
return self.config.module_completer.get_completions(line)

Expand Down
28 changes: 20 additions & 8 deletions Lib/test/test_pyrepl/test_pyrepl.py
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,14 @@ def test_func(self):

class TestPyReplModuleCompleter(TestCase):
def setUp(self):
import importlib
# Make iter_modules() search only the standard library.
# This makes the test more reliable in case there are
# other user packages/scripts on PYTHONPATH which can
# interfere with the completions.
lib_path = os.path.dirname(importlib.__path__[0])
self._saved_sys_path = sys.path
sys.path = [lib_path]

def tearDown(self):
sys.path = self._saved_sys_path
Expand All @@ -930,14 +937,6 @@ def prepare_reader(self, events, namespace):
return reader

def test_import_completions(self):
import importlib
# Make iter_modules() search only the standard library.
# This makes the test more reliable in case there are
# other user packages/scripts on PYTHONPATH which can
# intefere with the completions.
lib_path = os.path.dirname(importlib.__path__[0])
sys.path = [lib_path]

cases = (
("import path\t\n", "import pathlib"),
("import importlib.\t\tres\t\n", "import importlib.resources"),
Expand Down Expand Up @@ -988,6 +987,19 @@ def test_invalid_identifiers(self):
output = reader.readline()
self.assertEqual(output, expected)

def test_no_fallback_on_regular_completion(self):
cases = (
("import pri\t\n", "import pri"),
("from pri\t\n", "from pri"),
("from typing import Na\t\n", "from typing import Na"),
)
for code, expected in cases:
with self.subTest(code=code):
events = code_to_events(code)
reader = self.prepare_reader(events, namespace={})
output = reader.readline()
self.assertEqual(output, expected)

def test_get_path_and_prefix(self):
cases = (
('', ('', '')),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
When auto-completing an import in the :term:`REPL`, finding no candidates
now issues no suggestion, rather than suggestions from the current namespace.
Loading