Skip to content

Commit 1909b7b

Browse files
pablogsalambv
authored andcommitted
gh-111201: Speed up paste mode in the REPL (#119341)
Co-authored-by: Łukasz Langa <[email protected]>
1 parent dbff1f1 commit 1909b7b

File tree

6 files changed

+21
-16
lines changed

6 files changed

+21
-16
lines changed

Lib/_pyrepl/commands.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -461,18 +461,17 @@ def do(self) -> None:
461461
class paste_mode(Command):
462462

463463
def do(self) -> None:
464-
if not self.reader.paste_mode:
465-
self.reader.was_paste_mode_activated = True
466464
self.reader.paste_mode = not self.reader.paste_mode
467465
self.reader.dirty = True
468466

469467

470468
class enable_bracketed_paste(Command):
471469
def do(self) -> None:
472470
self.reader.paste_mode = True
473-
self.reader.was_paste_mode_activated = True
471+
self.reader.in_bracketed_paste = True
474472

475473
class disable_bracketed_paste(Command):
476474
def do(self) -> None:
477475
self.reader.paste_mode = False
476+
self.reader.in_bracketed_paste = False
478477
self.reader.dirty = True

Lib/_pyrepl/reader.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ def disp_str(buffer: str) -> tuple[str, list[int]]:
5454
b: list[int] = []
5555
s: list[str] = []
5656
for c in buffer:
57-
if unicodedata.category(c).startswith("C"):
57+
if ord(c) > 128 and unicodedata.category(c).startswith("C"):
5858
c = r"\u%04x" % ord(c)
5959
s.append(c)
6060
b.append(wlen(c))
@@ -225,7 +225,7 @@ class Reader:
225225
dirty: bool = False
226226
finished: bool = False
227227
paste_mode: bool = False
228-
was_paste_mode_activated: bool = False
228+
in_bracketed_paste: bool = False
229229
commands: dict[str, type[Command]] = field(default_factory=make_default_commands)
230230
last_command: type[Command] | None = None
231231
syntax_table: dict[str, int] = field(default_factory=make_default_syntax_table)
@@ -454,7 +454,7 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str:
454454
elif "\n" in self.buffer:
455455
if lineno == 0:
456456
prompt = self.ps2
457-
elif lineno == self.buffer.count("\n"):
457+
elif self.ps4 and lineno == self.buffer.count("\n"):
458458
prompt = self.ps4
459459
else:
460460
prompt = self.ps3
@@ -617,7 +617,7 @@ def do_cmd(self, cmd: tuple[str, list[str]]) -> None:
617617

618618
self.after_command(command)
619619

620-
if self.dirty:
620+
if self.dirty and not self.in_bracketed_paste:
621621
self.refresh()
622622
else:
623623
self.update_cursor()

Lib/_pyrepl/readline.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -328,7 +328,7 @@ def input(self, prompt: object = "") -> str:
328328
reader.ps1 = str(prompt)
329329
return reader.readline(startup_hook=self.startup_hook)
330330

331-
def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> tuple[str, bool]:
331+
def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> str:
332332
"""Read an input on possibly multiple lines, asking for more
333333
lines as long as 'more_lines(unicodetext)' returns an object whose
334334
boolean value is true.
@@ -337,14 +337,15 @@ def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) ->
337337
saved = reader.more_lines
338338
try:
339339
reader.more_lines = more_lines
340-
reader.ps1 = reader.ps2 = ps1
341-
reader.ps3 = reader.ps4 = ps2
340+
reader.ps1 = ps1
341+
reader.ps2 = ps1
342+
reader.ps3 = ps2
343+
reader.ps4 = ""
342344
with warnings.catch_warnings(action="ignore"):
343-
return reader.readline(), reader.was_paste_mode_activated
345+
return reader.readline()
344346
finally:
345347
reader.more_lines = saved
346348
reader.paste_mode = False
347-
reader.was_paste_mode_activated = False
348349

349350
def parse_and_bind(self, string: str) -> None:
350351
pass # XXX we don't support parsing GNU-readline-style init files

Lib/_pyrepl/simple_interact.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ def _strip_final_indent(text: str) -> str:
6262
"quit": _sitebuiltins.Quitter('quit' ,''),
6363
"copyright": _sitebuiltins._Printer('copyright', sys.copyright),
6464
"help": "help",
65+
"clear": "clear_screen",
6566
}
6667

6768
class InteractiveColoredConsole(code.InteractiveConsole):
@@ -163,7 +164,7 @@ def more_lines(unicodetext: str) -> bool:
163164
ps1 = getattr(sys, "ps1", ">>> ")
164165
ps2 = getattr(sys, "ps2", "... ")
165166
try:
166-
statement, contains_pasted_code = multiline_input(more_lines, ps1, ps2)
167+
statement = multiline_input(more_lines, ps1, ps2)
167168
except EOFError:
168169
break
169170

Lib/_pyrepl/utils.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
import re
22
import unicodedata
3+
import functools
34

45
ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]")
56

67

8+
@functools.cache
79
def str_width(c: str) -> int:
10+
if ord(c) < 128:
11+
return 1
812
w = unicodedata.east_asian_width(c)
913
if w in ('N', 'Na', 'H', 'A'):
1014
return 1
@@ -13,6 +17,6 @@ def str_width(c: str) -> int:
1317

1418
def wlen(s: str) -> int:
1519
length = sum(str_width(i) for i in s)
16-
1720
# remove lengths of any escape sequences
18-
return length - sum(len(i) for i in ANSI_ESCAPE_SEQUENCE.findall(s))
21+
sequence = ANSI_ESCAPE_SEQUENCE.findall(s)
22+
return length - sum(len(i) for i in sequence)

Lib/test/test_pyrepl/test_pyrepl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,7 @@ def test_func(self):
578578
reader = self.prepare_reader(events, namespace)
579579
mock_get_reader.return_value = reader
580580
output = readline_multiline_input(more_lines, ">>>", "...")
581-
self.assertEqual(output[0], "dummy.test_func.__")
581+
self.assertEqual(output, "dummy.test_func.__")
582582
self.assertEqual(mock_stderr.getvalue(), "")
583583

584584

0 commit comments

Comments
 (0)