Skip to content

Commit 8d35dac

Browse files
committed
PyREPL: fix completion inserted if spaces in import statements
Fix `from math .<tab>` inserting `from math .ath.integer` instead of `from math.integer`
1 parent 3a7df63 commit 8d35dac

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

Lib/_pyrepl/_module_completer.py

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,9 @@
1111
from io import StringIO
1212
from contextlib import contextmanager
1313
from dataclasses import dataclass
14-
from itertools import chain
14+
from itertools import chain, takewhile
1515
from tokenize import TokenInfo
16-
16+
from .trace import trace
1717
TYPE_CHECKING = False
1818

1919
if TYPE_CHECKING:
@@ -82,7 +82,9 @@ def get_completions(self, line: str) -> tuple[list[str], CompletionAction | None
8282
if not result:
8383
return None
8484
try:
85-
return self.complete(*result)
85+
names, action = self.complete(*result)
86+
names = [self._remove_separated_words(line, c) for c in names]
87+
return names, action
8688
except Exception:
8789
# Some unexpected error occurred, make it look like
8890
# no completions are available
@@ -300,6 +302,26 @@ def _do_import() -> str | None:
300302

301303
return (prompt, _do_import)
302304

305+
def _remove_separated_words(self, line: str, completion: str) -> str:
306+
"""Remove completion parts imputed as separate words.
307+
308+
Needed because the completer insert any completion provided
309+
by replacing the stem (eg. word) currently imputed, if any.
310+
311+
Examples:
312+
- 'import foo.', 'foo.bar' -> 'foo.bar'
313+
- 'import foo .', 'foo.bar' -> '.bar'
314+
- 'from foo import ', 'bar' -> 'bar'
315+
- 'import x.x .x.', 'x.x.x.x' -> '.x.x'
316+
"""
317+
last_word = line.split(" ")[-1]
318+
if not last_word:
319+
return completion
320+
index = completion.rfind(last_word)
321+
if index > 0:
322+
return completion[index:]
323+
return completion
324+
303325

304326
class ImportParser:
305327
"""

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1176,6 +1176,12 @@ def test_completions(self):
11761176
("from importlib import res\t\n", "from importlib import resources"),
11771177
("from importlib.res\t import a\t\n", "from importlib.resources import abc"),
11781178
("from __phello__ import s\t\n", "from __phello__ import spam"), # frozen module
1179+
("import importlib .res\t\n", "import importlib .resources"),
1180+
("import importlib. res\t\n", "import importlib. resources"),
1181+
("import importlib . res\t\n", "import importlib . resources"),
1182+
("import importlib .metadata.\t\n", "import importlib .metadata.diagnose"),
1183+
("import importlib .metadata .\t\n", "import importlib .metadata .diagnose"),
1184+
("from importlib .resources .a\t\n", "from importlib .resources .abc"),
11791185
)
11801186
for code, expected in cases:
11811187
with self.subTest(code=code):
@@ -1564,8 +1570,13 @@ def test_parse(self):
15641570
('import a.b.c, foo.bar, ', (None, '')),
15651571
('from foo', ('foo', None)),
15661572
('from a.', ('a.', None)),
1573+
('from a .', ('a.', None)),
1574+
('from a .', ('a.', None)),
1575+
('from a .', ('a.', None)),
15671576
('from a.b', ('a.b', None)),
15681577
('from a.b.', ('a.b.', None)),
1578+
('from a. b.', ('a.b.', None)),
1579+
('from a . b .', ('a.b.', None)),
15691580
('from a.b.c', ('a.b.c', None)),
15701581
('from foo import ', ('foo', '')),
15711582
('from foo import a', ('foo', 'a')),
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix :term:`REPL` completions inserted incorrectly when there was spaces in
2+
import statements.

0 commit comments

Comments
 (0)