mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 21:51:50 +00:00 
			
		
		
		
	gh-111201: Speed up paste mode in the REPL (#119341)
Co-authored-by: Łukasz Langa <lukasz@langa.pl>
This commit is contained in:
		
							parent
							
								
									cd516cd1f5
								
							
						
					
					
						commit
						e6572e8f98
					
				
					 6 changed files with 21 additions and 16 deletions
				
			
		|  | @ -461,8 +461,6 @@ def do(self) -> None: | ||||||
| class paste_mode(Command): | class paste_mode(Command): | ||||||
| 
 | 
 | ||||||
|     def do(self) -> None: |     def do(self) -> None: | ||||||
|         if not self.reader.paste_mode: |  | ||||||
|             self.reader.was_paste_mode_activated = True |  | ||||||
|         self.reader.paste_mode = not self.reader.paste_mode |         self.reader.paste_mode = not self.reader.paste_mode | ||||||
|         self.reader.dirty = True |         self.reader.dirty = True | ||||||
| 
 | 
 | ||||||
|  | @ -470,9 +468,10 @@ def do(self) -> None: | ||||||
| class enable_bracketed_paste(Command): | class enable_bracketed_paste(Command): | ||||||
|     def do(self) -> None: |     def do(self) -> None: | ||||||
|         self.reader.paste_mode = True |         self.reader.paste_mode = True | ||||||
|         self.reader.was_paste_mode_activated = True |         self.reader.in_bracketed_paste = True | ||||||
| 
 | 
 | ||||||
| class disable_bracketed_paste(Command): | class disable_bracketed_paste(Command): | ||||||
|     def do(self) -> None: |     def do(self) -> None: | ||||||
|         self.reader.paste_mode = False |         self.reader.paste_mode = False | ||||||
|  |         self.reader.in_bracketed_paste = False | ||||||
|         self.reader.dirty = True |         self.reader.dirty = True | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ def disp_str(buffer: str) -> tuple[str, list[int]]: | ||||||
|     b: list[int] = [] |     b: list[int] = [] | ||||||
|     s: list[str] = [] |     s: list[str] = [] | ||||||
|     for c in buffer: |     for c in buffer: | ||||||
|         if unicodedata.category(c).startswith("C"): |         if ord(c) > 128 and unicodedata.category(c).startswith("C"): | ||||||
|             c = r"\u%04x" % ord(c) |             c = r"\u%04x" % ord(c) | ||||||
|         s.append(c) |         s.append(c) | ||||||
|         b.append(wlen(c)) |         b.append(wlen(c)) | ||||||
|  | @ -225,7 +225,7 @@ class Reader: | ||||||
|     dirty: bool = False |     dirty: bool = False | ||||||
|     finished: bool = False |     finished: bool = False | ||||||
|     paste_mode: bool = False |     paste_mode: bool = False | ||||||
|     was_paste_mode_activated: bool = False |     in_bracketed_paste: bool = False | ||||||
|     commands: dict[str, type[Command]] = field(default_factory=make_default_commands) |     commands: dict[str, type[Command]] = field(default_factory=make_default_commands) | ||||||
|     last_command: type[Command] | None = None |     last_command: type[Command] | None = None | ||||||
|     syntax_table: dict[str, int] = field(default_factory=make_default_syntax_table) |     syntax_table: dict[str, int] = field(default_factory=make_default_syntax_table) | ||||||
|  | @ -448,7 +448,7 @@ def get_prompt(self, lineno: int, cursor_on_line: bool) -> str: | ||||||
|         elif "\n" in self.buffer: |         elif "\n" in self.buffer: | ||||||
|             if lineno == 0: |             if lineno == 0: | ||||||
|                 prompt = self.ps2 |                 prompt = self.ps2 | ||||||
|             elif lineno == self.buffer.count("\n"): |             elif self.ps4 and lineno == self.buffer.count("\n"): | ||||||
|                 prompt = self.ps4 |                 prompt = self.ps4 | ||||||
|             else: |             else: | ||||||
|                 prompt = self.ps3 |                 prompt = self.ps3 | ||||||
|  | @ -611,7 +611,7 @@ def do_cmd(self, cmd: tuple[str, list[str]]) -> None: | ||||||
| 
 | 
 | ||||||
|         self.after_command(command) |         self.after_command(command) | ||||||
| 
 | 
 | ||||||
|         if self.dirty: |         if self.dirty and not self.in_bracketed_paste: | ||||||
|             self.refresh() |             self.refresh() | ||||||
|         else: |         else: | ||||||
|             self.update_cursor() |             self.update_cursor() | ||||||
|  |  | ||||||
|  | @ -328,7 +328,7 @@ def input(self, prompt: object = "") -> str: | ||||||
|         reader.ps1 = str(prompt) |         reader.ps1 = str(prompt) | ||||||
|         return reader.readline(startup_hook=self.startup_hook) |         return reader.readline(startup_hook=self.startup_hook) | ||||||
| 
 | 
 | ||||||
|     def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> tuple[str, bool]: |     def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> str: | ||||||
|         """Read an input on possibly multiple lines, asking for more |         """Read an input on possibly multiple lines, asking for more | ||||||
|         lines as long as 'more_lines(unicodetext)' returns an object whose |         lines as long as 'more_lines(unicodetext)' returns an object whose | ||||||
|         boolean value is true. |         boolean value is true. | ||||||
|  | @ -337,14 +337,15 @@ def multiline_input(self, more_lines: MoreLinesCallable, ps1: str, ps2: str) -> | ||||||
|         saved = reader.more_lines |         saved = reader.more_lines | ||||||
|         try: |         try: | ||||||
|             reader.more_lines = more_lines |             reader.more_lines = more_lines | ||||||
|             reader.ps1 = reader.ps2 = ps1 |             reader.ps1 = ps1 | ||||||
|             reader.ps3 = reader.ps4 = ps2 |             reader.ps2 = ps1 | ||||||
|  |             reader.ps3 = ps2 | ||||||
|  |             reader.ps4 = "" | ||||||
|             with warnings.catch_warnings(action="ignore"): |             with warnings.catch_warnings(action="ignore"): | ||||||
|                 return reader.readline(), reader.was_paste_mode_activated |                 return reader.readline() | ||||||
|         finally: |         finally: | ||||||
|             reader.more_lines = saved |             reader.more_lines = saved | ||||||
|             reader.paste_mode = False |             reader.paste_mode = False | ||||||
|             reader.was_paste_mode_activated = False |  | ||||||
| 
 | 
 | ||||||
|     def parse_and_bind(self, string: str) -> None: |     def parse_and_bind(self, string: str) -> None: | ||||||
|         pass  # XXX we don't support parsing GNU-readline-style init files |         pass  # XXX we don't support parsing GNU-readline-style init files | ||||||
|  |  | ||||||
|  | @ -62,6 +62,7 @@ def _strip_final_indent(text: str) -> str: | ||||||
|     "quit": _sitebuiltins.Quitter('quit' ,''), |     "quit": _sitebuiltins.Quitter('quit' ,''), | ||||||
|     "copyright": _sitebuiltins._Printer('copyright', sys.copyright), |     "copyright": _sitebuiltins._Printer('copyright', sys.copyright), | ||||||
|     "help": "help", |     "help": "help", | ||||||
|  |     "clear": "clear_screen", | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| class InteractiveColoredConsole(code.InteractiveConsole): | class InteractiveColoredConsole(code.InteractiveConsole): | ||||||
|  | @ -163,7 +164,7 @@ def more_lines(unicodetext: str) -> bool: | ||||||
|             ps1 = getattr(sys, "ps1", ">>> ") |             ps1 = getattr(sys, "ps1", ">>> ") | ||||||
|             ps2 = getattr(sys, "ps2", "... ") |             ps2 = getattr(sys, "ps2", "... ") | ||||||
|             try: |             try: | ||||||
|                 statement, contains_pasted_code = multiline_input(more_lines, ps1, ps2) |                 statement = multiline_input(more_lines, ps1, ps2) | ||||||
|             except EOFError: |             except EOFError: | ||||||
|                 break |                 break | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1,10 +1,14 @@ | ||||||
| import re | import re | ||||||
| import unicodedata | import unicodedata | ||||||
|  | import functools | ||||||
| 
 | 
 | ||||||
| ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]") | ANSI_ESCAPE_SEQUENCE = re.compile(r"\x1b\[[ -@]*[A-~]") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | @functools.cache | ||||||
| def str_width(c: str) -> int: | def str_width(c: str) -> int: | ||||||
|  |     if ord(c) < 128: | ||||||
|  |         return 1 | ||||||
|     w = unicodedata.east_asian_width(c) |     w = unicodedata.east_asian_width(c) | ||||||
|     if w in ('N', 'Na', 'H', 'A'): |     if w in ('N', 'Na', 'H', 'A'): | ||||||
|         return 1 |         return 1 | ||||||
|  | @ -13,6 +17,6 @@ def str_width(c: str) -> int: | ||||||
| 
 | 
 | ||||||
| def wlen(s: str) -> int: | def wlen(s: str) -> int: | ||||||
|     length = sum(str_width(i) for i in s) |     length = sum(str_width(i) for i in s) | ||||||
| 
 |  | ||||||
|     # remove lengths of any escape sequences |     # remove lengths of any escape sequences | ||||||
|     return length - sum(len(i) for i in ANSI_ESCAPE_SEQUENCE.findall(s)) |     sequence = ANSI_ESCAPE_SEQUENCE.findall(s) | ||||||
|  |     return length - sum(len(i) for i in sequence) | ||||||
|  |  | ||||||
|  | @ -578,7 +578,7 @@ def test_func(self): | ||||||
|         reader = self.prepare_reader(events, namespace) |         reader = self.prepare_reader(events, namespace) | ||||||
|         mock_get_reader.return_value = reader |         mock_get_reader.return_value = reader | ||||||
|         output = readline_multiline_input(more_lines, ">>>", "...") |         output = readline_multiline_input(more_lines, ">>>", "...") | ||||||
|         self.assertEqual(output[0], "dummy.test_func.__") |         self.assertEqual(output, "dummy.test_func.__") | ||||||
|         self.assertEqual(mock_stderr.getvalue(), "") |         self.assertEqual(mock_stderr.getvalue(), "") | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Pablo Galindo Salgado
						Pablo Galindo Salgado