classification
Title: Interactive mode gives sys.ps2 not sys.ps1 after comment-only line
Type: behavior Stage:
Components: Interpreter Core Versions: Python 3.7, Python 3.6, Python 3.5, Python 2.7
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: Nosy List: JDLH, eryksun, terry.reedy
Priority: normal Keywords:

Created on 2017-02-14 20:34 by JDLH, last changed 2017-02-18 02:29 by terry.reedy.

Messages (4)
msg287799 - (view) Author: Jim DeLaHunt (JDLH) * Date: 2017-02-14 20:34
When you run the Python interpreter in interactive mode, get a sys.ps1 prompt (`...`), and type a comment-only or white-space-only line, the interpreter responds with a sys.ps2 prompt (`...`), instead of a sys.ps1 prompt. This seems wrong.

For example: 

% ./python.exe 
Python 3.7.0a0 (default, Feb 13 2017, 16:27:07) 
[GCC 4.2.1 Compatible Apple LLVM 7.0.2 (clang-700.1.81)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> # comment-only    <<-- line A
...                   <<-- line B
>>>                   <<-- line C 
>>>                   <<-- line D 

Line A is a comment-only line entered by the user. The interpreter responds with the sys.ps2 prompt (`...`), in line B. Line C is a null line (user enters only the Enter key, nothing else). The interpreter responds with a sys.ps1 prompt (`...`).  

If user enters a white-space-only line at a sys.ps1 prompt, the interpreter responds the same as if it were a comment-only line, with a sys.ps2 prompt (line B).

The expected expected interpreter behaviour in response to a  comment-only line or a white-space-only line, not in the midst of a compound statement, is that it be the same as to a null line, with a sys.ps1 prompt. 

In the context of a compound statement, the expected interpreter behaviour is that a comment-only line or a white-space-only line continue the compound statement. Entering a null line in a compound statement ends the compound statement.

Another way to phrase it: the expected interpreter behaviour is that a comment-only line or white-space-only line not change the interpreter prompt: if it was sys.ps1, it remains so; if it was sys.ps2, it remains so.

I have reproduced this behaviour on my build of cpython 3.7.a0, python 3.6 from MacPorts, python 2.7 from MacPorts, all on Mac OS X.  I see the same behaviour in the interactive shell at python.org/shell .

This makes me suspect that the Python design says my expectations are wrong. In that case, the current behaviour certainly is surprising. It should be documented.  I haven't found such documentation yet.
msg287823 - (view) Author: Eryk Sun (eryksun) * Date: 2017-02-15 07:11
For the tokenizer, a blank line is "[a] logical line that contains only spaces, tabs, formfeeds and possibly a comment" [1]. A blank line is normally ignored, except in the REPL an entirely blank line (i.e. no whitespace or comment) is used to end a multi-line statement.

This behavior is coded in Parser/tokenizer.c in tok_get(). After removing leading whitespace to get the indentation level, it decides whether the line should be ignored as blank as follows:

        if (c == '#' || c == '\n') {
            /* Lines with only whitespace and/or comments
               shouldn't affect the indentation and are
               not passed to the parser as NEWLINE tokens,
               except *totally* empty lines in interactive
               mode, which signal the end of a command group. */
            if (col == 0 && c == '\n' && tok->prompt != NULL) {
                blankline = 0; /* Let it through */
            }
            else {
                blankline = 1; /* Ignore completely */
            }
            /* We can't jump back right here since we still
               may need to skip to the end of a comment */
        }

The tokenizer switches to the ps2 prompt unconditionally, even if the first line was ignored as a blank line. One can argue that this behavior is outside of the norm for a shell or REPL. For example, bash doesn't switch to its PS2 prompt after ignoring an initial blank line. On the other hand, the interpreter is correctly conveying that it's still tokenizing the input; it hasn't compiled or executed any code.

[1]: https://docs.python.org/3/reference/lexical_analysis.html#blank-lines
msg287896 - (view) Author: Jim DeLaHunt (JDLH) * Date: 2017-02-15 23:10
Thank you for the analysis, Eryk Sun.

You wrote, "the interpreter is correctly conveying that it's still tokenizing the input; it hasn't compiled or executed any code." I think one important question for this issue is, what meaning should the choice of prompt convey?  That breaks down into, 1. what meaning does the user believe it conveys, and 2. what meaning does the present behaviour of the code imply?

"it's still tokenizing the input" translates to a meaning, sys.ps1 indicates the interpreter just finished executing and has no input, while sys.ps2 indicates the interpreter is tokenizing input and awaiting a chance to execute.

I think my intuition is, and the bash shell's behaviour says, sys.ps1 indicates the interpreter is ready to start a new statement, and sys.ps2 indicates the interpreter is in the midst of a compound statement.
msg288054 - (view) Author: Terry J. Reedy (terry.reedy) * (Python committer) Date: 2017-02-18 02:29
If src is effectively blank, compile(src, filename, mode) raises SyntaxError if mode is 'single' but not if it is 'exec'.  I believe IDLE compiles with 'single', but it has the behavior Jim (and I) expect and consider correct, printing '>>>' after effectively blank lines.  This is because IDLE uses code.InteractiveInterpreter, which uses codeop.compile_command, which uses codeop._maybe_compile, which replaces effectively blank statements with 'pass'.  compile('pass', '', 'single') returns a do-nothing code object.  The C-coded interactive interpreter is doing something else.
History
Date User Action Args
2017-02-18 02:29:24terry.reedysetnosy: + terry.reedy
messages: + msg288054
2017-02-15 23:10:39JDLHsetmessages: + msg287896
2017-02-15 07:12:00eryksunsetnosy: + eryksun
messages: + msg287823
2017-02-14 20:34:39JDLHcreate