Skip to content

Pasting into new REPL doesn't end continuations on blank line, interferes with "follow-along" examples #133972

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ferdnyc opened this issue May 13, 2025 · 4 comments
Labels
type-bug An unexpected behavior, bug, or error

Comments

@ferdnyc
Copy link
Contributor

ferdnyc commented May 13, 2025

Bug report

Bug description:

This is somewhat related to #131217, although I don't know if the associated PR would affect it.

When pasting into the new REPL, the pasted text is treated as one long input. That seems like a good thing, but it interferes with pasting text that's supposed to show output between the statements. Many such texts are found in our own documentation, as examples. For example (no pun):

The Format examples section of the string-formatting docs contains this example block:

>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
'a, b, c'
>>> '{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only
'a, b, c'
>>> '{2}, {1}, {0}'.format('a', 'b', 'c')
'c, b, a'
>>> '{2}, {1}, {0}'.format(*'abc')      # unpacking argument sequence
'c, b, a'
>>> '{0}{1}{0}'.format('abra', 'cad')   # arguments' indices can be repeated
'abracadabra'

When copied (using the handy new "Copy" button), the copied text will be:

'{0}, {1}, {2}'.format('a', 'b', 'c')

'{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only

'{2}, {1}, {0}'.format('a', 'b', 'c')

'{2}, {1}, {0}'.format(*'abc')      # unpacking argument sequence

'{0}{1}{0}'.format('abra', 'cad')   # arguments' indices can be repeated

With the old REPL, that could be pasted into an interactive session to "re-create" the example. If I paste into Python 3.11, I get this:

>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
'a, b, c'
>>> 
>>> '{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only
'a, b, c'
>>> 
>>> '{2}, {1}, {0}'.format('a', 'b', 'c')
'c, b, a'
>>> 
>>> '{2}, {1}, {0}'.format(*'abc')      # unpacking argument sequence
'c, b, a'
>>> 
>>> '{0}{1}{0}'.format('abra', 'cad')   # arguments' indices can be repeated

...I just have to hit Enter one last time to see the output of the last statement pasted.

But with the Python 3.13 REPL, that will paste as this:

>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
... 
... '{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only
... 
... '{2}, {1}, {0}'.format('a', 'b', 'c')
... 
... '{2}, {1}, {0}'.format(*'abc')      # unpacking argument sequence
... 
... '{0}{1}{0}'.format('abra', 'cad')   # arguments' indices can be repeated
... 

...Hitting Enter gets me only the result of the last statement, but not the others.

The ability to paste texts that include blank lines is very useful in certain situations, don't get me wrong. But at other times it can detract from the utility of the REPL. I don't have a good solution to balancing those concerns.

CPython versions tested on:

3.13, 3.11

Operating systems tested on:

Linux

@ferdnyc ferdnyc added the type-bug An unexpected behavior, bug, or error label May 13, 2025
@StanFromIreland
Copy link
Contributor

The ability to paste texts that include blank lines is very useful in certain situations, don't get me wrong. But at other times it can detract from the utility of the REPL. I don't have a good solution to balancing those concerns.

This is my chief concern as both options make logical sense in different contexts. @ambv / @pablogsal was this a design choice (and this should be closed) or was this accidental (and this should be fixed)?

@ambv
Copy link
Contributor

ambv commented May 13, 2025

This is a deliberate change due to multiline editing. A very early bug report was that pasting this didn't work:

def function():
    """qweqwe"""

    body_of_the_function

Executing the function up to the empty line was making the body_of_the_function part crash with an IndentationError.

If you want to rely on previous behavior, use the PYTHON_BASIC_REPL=1 environment variable.

@ambv ambv closed this as not planned Won't fix, can't repro, duplicate, stale May 13, 2025
@ferdnyc
Copy link
Contributor Author

ferdnyc commented May 13, 2025

@ambv That's fair, though... devil's advocate... isn't it theoretically possible to support both?

The same way the REPL has always been able to detect that this input should be continued:

>>> len(
...

it seems like there would be a detectable difference between this:

>>> def function():
...     """docstring"""
...
...

and this:

>>> len(mylist)
...

The context of the first is an indented block, which doesn't necessarily have to be terminated on an empty line, the same way this isn't:

>>> len(
...
...
...

You can keep hitting Enter there forever (even in the old REPL), because the REPL knows it's waiting for that open-paren to be closed.

With the indented block, there is the complication that we eventually need a blank line to terminate it. (This is one of the advantages to using semicolons to terminate statements, or braces to enclose blocks.) But that's where the difference between pasted input and typed input could come in handy. Because, while we of course want this to be processed all as one statement when pasted:

>>> def function():
...     """docstring"""
... 
...     return 2 + 2
...

Typing another Enter interactively after that point should terminate it.

And by the same token, since the context for each of these is a completed statement:

>>> '{0}, {1}, {2}'.format('a', 'b', 'c')
...
>>> '{}, {}, {}'.format('a', 'b', 'c')  # 3.1+ only
...

It'd be desirable for the empty line to terminate it even if the input it pasted.

I'm not pretending it would be easy, and it may even be impossible (you certainly know the input parser far better than I), but it seems like there's potentially enough information there to make different decisions in different contexts. Just spitballing, really.

@ferdnyc
Copy link
Contributor Author

ferdnyc commented May 13, 2025

Because, while we of course want this to be processed all as one statement when pasted:

>>> def function():
...     """docstring"""
... 
...     return 2 + 2
...

Typing another Enter interactively after that point should terminate it.

And in fact, the new REPL already handles that blank line differently when it's interactive. While pasting the text above will (now) process it all as the same function definition, interactively typing the same thing cuts the function definition off at this point:

>>> def function():
...     """docstring"""
...
>>> 

Unless you indent the blank line, interactively it'll terminate the def.

So it doesn't seem too huge a leap to say that a pasted blank line during indented input could mean something different than a pasted blank line outside that same indented context.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type-bug An unexpected behavior, bug, or error
Projects
None yet
Development

No branches or pull requests

3 participants