GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — GH-130328: pasting in new REPL is slow on Windows by chris-eibl · Pull Request #132884 · python/cpython

Conversation

@chris-eibl

@chris-eibl chris-eibl commented Apr 24, 2025 •
edited by bedevere-app Bot
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member

The reason why pasting is so slow on Windows (especially in the legacy console case where virtual terminal mode - and thus bracketed paste - is disabled):

cpython/Lib/_pyrepl/reader.py

Lines 702 to 706 in 9f5994b

while True:
# We use the same timeout as in readline.c: 100ms
self.run_hooks()
self.console.wait(100)
event = self.console.get_event(block=False)

and

cpython/Lib/_pyrepl/windows_console.py

Lines 523 to 532 in 9f5994b

def wait(self, timeout: float | None) -> bool:
"""Wait for an event."""
# Poor man's Windows select loop
start_time = time.time()
while True:
if msvcrt.kbhit(): # type: ignore[attr-defined]
return True
if timeout and time.time() - start_time > timeout / 1000:
return False
time.sleep(0.01)

It is interesting, that msvcrt.kbhit() returns True in case of pasting, but if that weren't the case, we'd hit the 100ms timeout and would be even way slower. However, msvcrt.kbhit() is very slow in case of the legacy console, and it is called at least twice as often, because we get a key up and a key down event - but only a key down event in the virtual terminal case. If the pasted input contains upper case letters, we additionally get a "shift key pressed" event - so in the worst case, msvcrt.kbhit() gets called 3 times more often (and each call is slower).

Output of python.bat -m cProfile -m _pyrepl when pasting the "test value" given in the OP for a legacy console:

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     50/1    0.010    0.000   17.202   17.202 {built-in method builtins.exec}
        1    0.000    0.000   17.202   17.202 <frozen runpy>:201(run_module)
        1    0.000    0.000   17.199   17.199 <frozen runpy>:65(_run_code)
        1    0.000    0.000   17.199   17.199 __main__.py:1(<module>)
        1    0.000    0.000   17.112   17.112 main.py:24(interactive_console)
        1    0.000    0.000   17.112   17.112 simple_interact.py:99(run_multiline_interactive_console)
        5    0.000    0.000   17.111    3.422 readline.py:375(multiline_input)
        5    0.004    0.001   17.111    3.422 reader.py:741(readline)
     3090    0.036    0.000   17.103    0.006 reader.py:694(handle1)
     6210    0.013    0.000   13.714    0.002 windows_console.py:523(wait)
     6236   13.433    0.002   13.433    0.002 {built-in method msvcrt.kbhit}

Here the relevant line in case of virtual terminal:

  3270    0.554    0.000    0.554    0.000 {built-in method msvcrt.kbhit}

The fix is to use WaitForSingleObject

    def wait(self, timeout: float | None) -> bool:
        """Wait for an event."""
        ret = WaitForSingleObject(InHandle, int(timeout))
        if ret == WAIT_FAILED:
            raise WinError(ctypes.get_last_error())

which speeds up especially the legacy console (times in seconds):

legacy terminal virtual terminal
before 15.6 0.89
after 1.8 0.26

Note, see also #132440 (comment):

  • legacy terminal: manually start cmd.exe. Timings gotten by pasting
import time
t1 = time.time()
<"test value" given in the OP>
print(time.time() - t1)
  • virtual terminal: power shell via Windows terminal. Timings gotton by temporarily patching commands.py:
import time
class enable_bracketed_paste(Command):
    def do(self) -> None:
        self.reader.bp_begin = time.perf_counter()
        self.reader.paste_mode = True
        self.reader.in_bracketed_paste = True

class disable_bracketed_paste(Command):
    def do(self) -> None:
        print("bracketed_paste took %5.2f s" % (time.perf_counter() - self.reader.bp_begin, ))
        self.reader.paste_mode = False
        self.reader.in_bracketed_paste = False
        self.reader.dirty = True
  • Issue: Pasting in new REPL is slow #130328

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
chris-eibl added 2 commits April 24, 2025 18:02
@chris-eibl
fix WindowsConsole.wait()
0a38c2e
@chris-eibl
blurb it
05d26ff
@chris-eibl
chris-eibl requested review from ambv, lysnikolaou and pablogsal as code owners April 24, 2025 17:22
@bedevere-app bedevere-app Bot added the awaiting review label Apr 24, 2025
@bedevere-app bedevere-app Bot mentioned this pull request Apr 24, 2025
Pasting in new REPL is slow #130328
Closed
@chris-eibl chris-eibl added the needs backport to 3.13 only security fixes label Apr 24, 2025
@sergey-miryanov

sergey-miryanov commented Apr 24, 2025

Copy link
Copy Markdown
Contributor

Good analysis and speedup! 👍

chris-eibl reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chris-eibl
fix timeout=None
cad6bfb
chris-eibl
chris-eibl commented Apr 24, 2025
View reviewed changes
Comment thread Lib/_pyrepl/windows_console.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

chris-eibl added 2 commits April 24, 2025 19:53
@chris-eibl
fix mypy for ctypes.get_last_error
3d1da66
@chris-eibl
fix return value of wait() and use it in _read_input()
4098e3d
chris-eibl
chris-eibl commented Apr 24, 2025
View reviewed changes
Comment thread Lib/_pyrepl/windows_console.py
if not events.value:
return None
if not block and not self.wait(timeout=0):
return None

@chris-eibl chris-eibl Apr 24, 2025

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we are nicely in sync with unix_console.py

cpython/Lib/_pyrepl/unix_console.py

Lines 400 to 401 in e1c09ff

if not block and not self.wait(timeout=0):
return None

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
tomasr8
tomasr8 reviewed Apr 24, 2025
View reviewed changes
Comment thread Lib/_pyrepl/windows_console.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@chris-eibl chris-eibl mentioned this pull request Apr 24, 2025
GH-130328: further speedup of pasting in new REPL on Windows by reading in chunks #132889
Closed
@chris-eibl chris-eibl added OS-windows topic-repl Related to the interactive shell labels Apr 24, 2025
@Fidget-Spinner

Fidget-Spinner commented Apr 24, 2025

Copy link
Copy Markdown
Member

We generally don't backport perf fixes unless they lead to a denial-of-service problem (so usually like an exponential-time algorithm), or result in a serious bug. So I'm removing the backport label.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Fidget-Spinner Fidget-Spinner removed the needs backport to 3.13 only security fixes label Apr 24, 2025
@Fidget-Spinner

Fidget-Spinner commented Apr 24, 2025

Copy link
Copy Markdown
Member

On second thought, "slow" is perhaps an understatement. So once this is merged, let's ping Thomas to ask for his permission to backport to 3.13

chris-eibl reacted with thumbs up emoji
All reactions
  • 👍 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chris-eibl

chris-eibl commented Apr 24, 2025 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Member Author

Yeah, in case of the legacy console pasting of the 30 + 3 lines given by the OP takes 15 seconds, and this brings it down to 1.8. Most people will hopefully use a virtual terminal, where the situation is much less severe. After all, the new REPL can be disabled using https://docs.python.org/3/using/cmdline.html#envvar-PYTHON_BASIC_REPL.

3 more lines added by me for easier benchmarking #130328 (comment)

import time
t1 = time.time()
"test value" given in the OP

"""1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"""

print(time.time() - t1)
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@pablogsal

pablogsal commented Apr 25, 2025

Copy link
Copy Markdown
Member

If @ambv has not checked this this in a couple of days ping me and I will do a pass and land it. Thanks a lot for looking into this @chris-eibl ❤️

chris-eibl reacted with heart emoji
All reactions
  • ❤️ 1 reaction

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chris-eibl chris-eibl mentioned this pull request Apr 25, 2025
GH-132439: Fix REPL swallowing characters entered with AltGr on cmd.exe #132440
Merged
chris-eibl added 2 commits April 28, 2025 16:54
@chris-eibl
use self.paste_mode instead of self.in_bracketed_paste
bba26c9
in class Reader
@chris-eibl
Revert "use self.paste_mode instead of self.in_bracketed_paste"
3c0cae9
This reverts commit bba26c9.
@ambv
ambv merged commit acb222c into python:main Apr 29, 2025
@bedevere-app bedevere-app Bot removed the awaiting review label Apr 29, 2025
@ambv

ambv commented Apr 29, 2025

Copy link
Copy Markdown
Contributor

Thanks for the fix, Chris!

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@Fidget-Spinner

Fidget-Spinner commented Apr 29, 2025

Copy link
Copy Markdown
Member

@Yhg1s seeking permission to backport this to 3.13.

According to Chris, it takes 15 seconds to paste 58 lines in the new repl in 3.13. Do you think that's slow enough to be worth backporting?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@chris-eibl
chris-eibl deleted the fix_wait branch May 2, 2025 14:01
@chris-eibl

chris-eibl commented May 20, 2025

Copy link
Copy Markdown
Member Author

@Yhg1s friendly ping and also asking @ambv whether this is worth backporting to 3.13?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@KristofferC KristofferC mentioned this pull request Oct 9, 2025
add syntax highlighting to the REPL input JuliaLang/julia#59778
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@tomasr8 tomasr8 tomasr8 left review comments
@pablogsal pablogsal Awaiting requested review from pablogsal pablogsal is a code owner
@lysnikolaou lysnikolaou Awaiting requested review from lysnikolaou lysnikolaou is a code owner
@ambv ambv Awaiting requested review from ambv ambv is a code owner

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

OS-windows topic-repl Related to the interactive shell

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

6 participants

@chris-eibl @sergey-miryanov @Fidget-Spinner @pablogsal @ambv @tomasr8
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.