Skip to content
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