https://github.com/python/cpython/commit/96005d399e15f0e8d51ef133ef2a4ff6c6f7d453
commit: 96005d399e15f0e8d51ef133ef2a4ff6c6f7d453
branch: 3.15
author: Miss Islington (bot) <[email protected]>
committer: ambv <[email protected]>
date: 2026-10-20T18:49:23-07:00
summary:

[3.15] gh-69605: fix PyREPL completions inserted for import statements with 
spaces (GH-148629) (#159989)

Fix `from math .<tab>` inserting `from math .ath.integer` instead of `from 
math.integer`
(cherry picked from commit 488ce0f050d798ff0553ac6b52e7bccb73b60017)

Co-authored-by: Loïc Simon <[email protected]>

files:
A 
Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-21-55-45.gh-issue-69605.NwoP6l.rst
M Lib/_pyrepl/_module_completer.py
M Lib/test/test_pyrepl/test_pyrepl.py

diff --git a/Lib/_pyrepl/_module_completer.py b/Lib/_pyrepl/_module_completer.py
index 17bf5cdc819542d..2381f3d4b591559 100644
--- a/Lib/_pyrepl/_module_completer.py
+++ b/Lib/_pyrepl/_module_completer.py
@@ -88,7 +88,11 @@ def get_completions(
         if not result:
             return None
         try:
-            return self.complete(*result, include_values=include_values)
+            names, values, action = self.complete(
+                *result, include_values=include_values
+            )
+            names = [self._remove_separated_words(line, c) for c in names]
+            return names, values, action
         except Exception:
             # Some unexpected error occurred, make it look like
             # no completions are available
@@ -341,6 +345,26 @@ def _do_import() -> str | None:
 
         return (prompt, _do_import)
 
+    def _remove_separated_words(self, line: str, completion: str) -> str:
+        """Remove completion parts imputed as separate words.
+
+        Needed because the completer insert any completion provided
+        by replacing the stem (eg. word) currently imputed, if any.
+
+        Examples:
+            - 'import foo.', 'foo.bar'    -> 'foo.bar'
+            - 'import foo  .', 'foo.bar'  -> '.bar'
+            - 'from foo import ', 'bar'   -> 'bar'
+            - 'import x.x .x.', 'x.x.x.x' -> '.x.x'
+        """
+        last_word = line.split(" ")[-1]
+        if not last_word:
+            return completion
+        index = completion.rfind(last_word)
+        if index > 0:
+            return completion[index:]
+        return completion
+
 
 class ImportParser:
     """
diff --git a/Lib/test/test_pyrepl/test_pyrepl.py 
b/Lib/test/test_pyrepl/test_pyrepl.py
index d748c07d60d2a4b..8ea9e6a7ff14887 100644
--- a/Lib/test/test_pyrepl/test_pyrepl.py
+++ b/Lib/test/test_pyrepl/test_pyrepl.py
@@ -1238,6 +1238,12 @@ def test_completions(self):
             ("from importlib import res\t\n", "from importlib import 
resources"),
             ("from importlib.res\t import a\t\n", "from importlib.resources 
import abc"),
             ("from __phello__ import s\t\n", "from __phello__ import spam"),  
# frozen module
+            ("import importlib .res\t\n", "import importlib .resources"),
+            ("import importlib. res\t\n", "import importlib. resources"),
+            ("import importlib . res\t\n", "import importlib . resources"),
+            ("import importlib  .metadata.\t\n", "import importlib  
.metadata.diagnose"),
+            ("import importlib .metadata   .\t\n", "import importlib .metadata 
  .diagnose"),
+            ("from importlib .resources .a\t\n", "from importlib .resources 
.abc"),
         )
         for code, expected in cases:
             with self.subTest(code=code):
@@ -1629,8 +1635,13 @@ def test_parse(self):
             ('import a.b.c, foo.bar, ', (None, '')),
             ('from foo', ('foo', None)),
             ('from a.', ('a.', None)),
+            ('from a .', ('a.', None)),
+            ('from a   .', ('a.', None)),
+            ('from a .', ('a.', None)),
             ('from a.b', ('a.b', None)),
             ('from a.b.', ('a.b.', None)),
+            ('from a. b.', ('a.b.', None)),
+            ('from a  .  b  .', ('a.b.', None)),
             ('from a.b.c', ('a.b.c', None)),
             ('from foo import ', ('foo', '')),
             ('from foo import a', ('foo', 'a')),
diff --git 
a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-21-55-45.gh-issue-69605.NwoP6l.rst
 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-21-55-45.gh-issue-69605.NwoP6l.rst
new file mode 100644
index 000000000000000..77491c6e5f62070
--- /dev/null
+++ 
b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-15-21-55-45.gh-issue-69605.NwoP6l.rst
@@ -0,0 +1,2 @@
+Fix :term:`REPL` completions inserted incorrectly when there was spaces in
+import statements.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to