https://github.com/python/cpython/commit/0e3bc962c6462f836751e35ef35fa837fd952550
commit: 0e3bc962c6462f836751e35ef35fa837fd952550
branch: main
author: Loïc Simon <loic.p...@gmail.com>
committer: ambv <luk...@langa.pl>
date: 2025-05-26T01:05:08+02:00
summary:

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

Co-authored-by: Loïc Simon <loic.si...@napta.io>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst
M Lib/_pyrepl/_module_completer.py
M Lib/_pyrepl/readline.py
M Lib/test/test_pyrepl/test_pyrepl.py

diff --git a/Lib/_pyrepl/_module_completer.py b/Lib/_pyrepl/_module_completer.py
index 494a501101a9b2..1e9462a42156d4 100644
--- a/Lib/_pyrepl/_module_completer.py
+++ b/Lib/_pyrepl/_module_completer.py
@@ -42,11 +42,11 @@ 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]:
+    def get_completions(self, line: str) -> list[str] | None:
         """Return the next possible import completions for 'line'."""
         result = ImportParser(line).parse()
         if not result:
-            return []
+            return None
         try:
             return self.complete(*result)
         except Exception:
diff --git a/Lib/_pyrepl/readline.py b/Lib/_pyrepl/readline.py
index 572eee520e53f3..9560ae779abfea 100644
--- a/Lib/_pyrepl/readline.py
+++ b/Lib/_pyrepl/readline.py
@@ -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
@@ -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)
 
diff --git a/Lib/test/test_pyrepl/test_pyrepl.py 
b/Lib/test/test_pyrepl/test_pyrepl.py
index abb4bd1bc25fb1..aa3a592766d6d1 100644
--- a/Lib/test/test_pyrepl/test_pyrepl.py
+++ b/Lib/test/test_pyrepl/test_pyrepl.py
@@ -918,7 +918,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
@@ -932,14 +939,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"),
@@ -1052,6 +1051,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 = (
             ('', ('', '')),
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst
new file mode 100644
index 00000000000000..7b7275fee69b9b
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2025-05-18-14-33-23.gh-issue-69605.ZMO49F.rst
@@ -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.

_______________________________________________
Python-checkins mailing list -- python-checkins@python.org
To unsubscribe send an email to python-checkins-le...@python.org
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: arch...@mail-archive.com

Reply via email to