GeezerVison is showing github.com — gh-157612: Strip prompts and output from pasted REPL sessions by johnslavik · Pull Request #157615 · python/cpython
Conversation
johnslavik
commented
Sep 16, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
Copy a REPL example from a web page and paste it straight into the new REPL:
>>> def double(value):
... return value * 2
...
>>> result = double(3)
>>> print(result)
6
At an empty >>> prompt, the REPL removes the copied prompts and output, leaving this code ready to edit:
def double(value):
return value * 2
result = double(3)
print(result)
Indentation stays intact. Nothing runs on paste, so you can change double(3) to double(5) before running it.
This also makes it easier to send students examples or copy code between two REPLs to compare different parameters. You no longer need to delete prompts and output by hand.
The terminal must support bracketed paste, which tells the REPL where a paste starts and ends. The input must be empty, and the paste must start with a standard >>> prompt. Pasting into existing code, a string, or an input() prompt keeps the text unchanged.
Rough edges
As discussed in the issue, this only recognizes >>> and ..., since those are the prompts most web examples use. Output that looks like a prompted code line will also be kept as code.
The pasted code runs as one block, so it can behave differently from entering each command separately. The issue notes differences involving sys.displayhook and builtins._.
Tests cover prompt and output removal, different line endings, pastes that should stay unchanged, and editing before running.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
edvilme
left a comment
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
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.
I think it should also consider cases in which the prompts have been changed. I think these would be few but still ?
E.g., (not endorsing changing the prompt, but something that could happen, especially in other tools modifying the REPL)
>>> import sys
>>> sys.ps1 = "py> "
py> # This is code I want to copy and paste later
py> class A(object):
... def __new__(self):
... raise TypeError("i do not exist")
...
py> class B(A):
... __new__ = object.__new__
... def __init__(self, x):
... self.x = x
...
py> B(1)
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
johnslavik
commented
Sep 16, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
We could, but I find that limiting this to >>> and ... only makes this feature simpler and easier to predict.
Do you personally customize your PS1 and would need that in this feature?
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
edvilme
commented
Sep 16, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
Not explicitly but it is documented behavior on the tutorial.
However, I was thinking more of tools/IDEs modifying PYTHONSTARTUP to alter the behavior of the REPL (VSCode does it -- although mostly to insert ANSI characters; but there could be other tools that do change it completely)
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
johnslavik
commented
Sep 16, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
However, I was thinking more of tools/IDEs modifying PYTHONSTARTUP to alter the behavior of the REPL (VSCode does it -- although mostly to insert ANSI characters; but there could be other tools that do change it completely)
-
VS Code uses >>>. We already match >>>. Seems to me that matching against sys.ps1 doesn't solve anything here.
-
Your second suggestion would raise a TypeError under the VS Code terminal with patched sys.ps11, since VS Code sets sys.ps1 to a non-str object, which by the way floods the terminal, preventing the 3.15.rc2+ REPL from starting in the first place:
Screen.Recording.2026-09-16.at.23.28.48.mov
Consequently, your first suggestion would never match under the VS Code terminal with patched sys.ps1.
although mostly to insert ANSI characters
Would a user paste something that contains ANSI sequences?
I was thinking more of tools/IDEs modifying PYTHONSTARTUP to alter the behavior of the REPL
What tools or IDEs modify sys.ps1 to something other than >>>? How would following their specific behaviors benefit this feature in CPython?
Footnotes
-
Assuming the bug that follows (that prevents the REPL from starting in the first place) is fixed. ↩
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
johnslavik
commented
Sep 16, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
Please also note that sys.ps1 can have leading lines which would make it more complicated to match. I'd really stick only with >>> and ... for now.
|
edvilme reacted with heart emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Oh you are right, didn't consider the case for it not being a string :)
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
ambv
commented
Oct 20, 2026
•
edited
Loading
Uh oh!
There was an error while loading. Please reload this page.
|
There's a few edge cases here that I don't like around how the thing that actually executes is not the thing that the person pasting the snippet expects:
-
Copying between two new REPLs (the PR's stated use case) can silently change code. When an input line is wider than the terminal, pyrepl draws a \ in the last column and continues on a new row with no prompt. Raw pty bytes plus a real terminal emulator (WezTerm) confirm that terminals copy that row as a separate line. The PR drops the row, and the leftover \ joins the line onto the next statement. Most wrapped pastes end in a visible SyntaxError instead. The silent cases are mainly long list or string literals, which is arguably worse.
-
Lines abandoned with Ctrl-C get run. Both REPLs leave an abandoned line on screen with its prompt, followed by KeyboardInterrupt. The PR keeps the line and drops the KeyboardInterrupt as output, so code the user chose not to run now runs.
>>> x = 1
>>> x = 2
KeyboardInterrupt
>>> x
1
-
Replay stops at the first exception, and the traceback hides it. On Enter, pyrepl runs the whole buffer as one input and stops at the first statement that raises (Lib/_pyrepl/console.py). Many docs and tutorial sessions deliberately show an error and then continue. The printed traceback is the one the session already shows, so the skipped statements go unnoticed.
- The PR's own test case (1 / 0, then print('hello')) never prints hello. The tests only
check the edit buffer, never what runs.
- In the tutorial dict session (datastructures.rst:576), the 8 statements after the KeyError are skipped and tel ends up wrong.
- About 59 doc blocks have this shape.
-
The strip also runs at the (Pdb) prompt (a regression and outside the documented scope). PdbPyReplInput also sets more_lines, so the PR's more_lines is not None check doesn't exclude pdb. There's some plausible conflicts with the Pdb command grammar here:
- q = …, quit = … or exit = … as the first statement quits pdb and ends the program. Under python -m pdb the process just exits with no confirmation.
- run = … / restart = … restart the program with sys.argv set to ['=', …].
- A one-line paste of >>> c continues the program.
- Baseline gives a harmless *** SyntaxError.
- pdb's command dispatch is old behaviour; the PR just makes a pasted session reach it.
There's more cases that are less concerning, but the state above makes me think that maybe this convenience is not worth it by default.
A plausible way out would be to discover the paste of a console session, and attach more info to the SyntaxError raised that would tell the user "To use Paste Mode (F3) to automatically strip >>> and ...". We would also need to address the backslash-wrapped lines handling, and at least document the exception handling difference.
|
johnslavik reacted with heart emoji
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
1 - fixed, 2 - KeyboardInterrupt prompts are stripped out, 3 - implemented the new flow with error overlays, 4 - feature no longer works in pdb.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
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.
In the current PR, generic commands know about ReadlineAlikeReader.paste_repl_sessions and can_strip_repl_prompts, and that reader imports Python-session parsing helpers from commands.py. The responsibility is split across both layers. I’d use the existing command registry to install reader-specific paste commands. ReadlineAlikeReader.post_init() already does this for maybe_accept and backspace_dedent.
Keep bracketed-paste collection generic. Then, specialise it in readline.py (specialise the F3 command there too). Finally, register both specializations in ReadlineAlikeReader.post_init().
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
johnslavik reacted with thumbs up emoji
|
A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.
Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
✨ I have made the requested changes; please review again ✨
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Thanks for making the requested changes!
@ambv: please review the changes made to this pull request.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
|
Merging main to fix a flake.
|
Sorry, something went wrong.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Assignees
No one assigned
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.
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.
|