Skip to content

Commit fe028c9

Browse files
committed
gh-111201: Speed up paste mode in the REPL
1 parent b7f45a9 commit fe028c9

File tree

5 files changed

+19
-17
lines changed

5 files changed

+19
-17
lines changed

Lib/_pyrepl/commands.py

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

460460
def do(self) -> None:
461-
if not self.reader.paste_mode:
462-
self.reader.was_paste_mode_activated = True
463461
self.reader.paste_mode = not self.reader.paste_mode
464462
self.reader.dirty = True
465463

466464

467465
class enable_bracketed_paste(Command):
468466
def do(self) -> None:
469467
self.reader.paste_mode = True
470-
self.reader.was_paste_mode_activated = True
468+
self.reader.in_bracketed_paste = True
471469

472470
class disable_bracketed_paste(Command):
473471
def do(self) -> None:
474472
self.reader.paste_mode = False
473+
self.reader.in_bracketed_paste = False
475474
self.reader.dirty = True

Lib/_pyrepl/reader.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ def disp_str(buffer: str) -> tuple[str, list[int]]:
5252
b: list[int] = []
5353
s: list[str] = []
5454
for c in buffer:
55-
if unicodedata.category(c).startswith("C"):
55+
if ord(c) > 128 and unicodedata.category(c).startswith("C"):
5656
c = r"\u%04x" % ord(c)
5757
s.append(c)
5858
b.append(wlen(c))
@@ -223,7 +223,7 @@ class Reader:
223223
dirty: bool = False
224224
finished: bool = False
225225
paste_mode: bool = False
226-
was_paste_mode_activated: bool = False
226+
in_bracketed_paste: bool = False
227227
commands: dict[str, type[Command]] = field(default_factory=make_default_commands)
228228
last_command: type[Command] | None = None
229229
syntax_table: dict[str, int] = field(default_factory=make_default_syntax_table)
@@ -422,7 +422,7 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str:
422422
elif "\n" in self.buffer:
423423
if lineno == 0:
424424
prompt = self.ps2
425-
elif lineno == self.buffer.count("\n"):
425+
elif self.ps4 and lineno == self.buffer.count("\n"):
426426
prompt = self.ps4
427427
else:
428428
prompt = self.ps3
@@ -585,7 +585,7 @@ def do_cmd(self, cmd: tuple[str, list[str]]) -> None:
585585

586586
self.after_command(command)
587587

588-
if self.dirty:
588+
if self.dirty and not self.in_bracketed_paste:
589589
self.refresh()
590590
else:
591591
self.update_cursor()

Lib/_pyrepl/readline.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ def input(self, prompt: object = "") -> str:
290290
reader.ps1 = str(prompt)
291291
return reader.readline(startup_hook=self.startup_hook)
292292

293-
def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> tuple[str, bool]:
293+
def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> str:
294294
"""Read an input on possibly multiple lines, asking for more
295295
lines as long as 'more_lines(unicodetext)' returns an object whose
296296
boolean value is true.
@@ -300,12 +300,12 @@ def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) ->
300300
try:
301301
reader.more_lines = more_lines
302302
reader.ps1 = reader.ps2 = ps1
303-
reader.ps3 = reader.ps4 = ps2
304-
return reader.readline(), reader.was_paste_mode_activated
303+
reader.ps3 = ps2
304+
reader.ps4 = ""
305+
return reader.readline()
305306
finally:
306307
reader.more_lines = saved
307308
reader.paste_mode = False
308-
reader.was_paste_mode_activated = False
309309

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

Lib/_pyrepl/simple_interact.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def _strip_final_indent(text: str) -> str:
6161
"quit": _sitebuiltins.Quitter('quit' ,''),
6262
"copyright": _sitebuiltins._Printer('copyright', sys.copyright),
6363
"help": "help",
64+
"clear": "clear_screen",
6465
}
6566

6667
class InteractiveColoredConsole(code.InteractiveConsole):
@@ -135,7 +136,7 @@ def more_lines(unicodetext: str) -> bool:
135136
ps1 = getattr(sys, "ps1", ">>> ")
136137
ps2 = getattr(sys, "ps2", "... ")
137138
try:
138-
statement, contains_pasted_code = multiline_input(more_lines, ps1, ps2)
139+
statement = multiline_input(more_lines, ps1, ps2)
139140
except EOFError:
140141
break
141142

@@ -144,10 +145,8 @@ def more_lines(unicodetext: str) -> bool:
144145

145146
input_name = f"<python-input-{input_n}>"
146147
linecache._register_code(input_name, statement, "<stdin>") # type: ignore[attr-defined]
147-
symbol = "single" if not contains_pasted_code else "exec"
148+
symbol = "single"
148149
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol=symbol) # type: ignore[call-arg]
149-
if contains_pasted_code and more:
150-
more = console.push(_strip_final_indent(statement), filename=input_name, _symbol="single") # type: ignore[call-arg]
151150
assert not more
152151
input_n += 1
153152
except KeyboardInterrupt:

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)

0 commit comments

Comments
 (0)