Skip to content
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

Inconsistent SyntaxError for print #78194

Closed
corona10 opened this issue Jun 30, 2018 · 33 comments
Closed

Inconsistent SyntaxError for print #78194

corona10 opened this issue Jun 30, 2018 · 33 comments
Labels
3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) release-blocker type-bug An unexpected behavior, bug, or error

Comments

@corona10
Copy link
Member

BPO 34013
Nosy @terryjreedy, @ncoghlan, @ericvsmith, @aroberge, @serhiy-storchaka, @vedgar, @ammaraskar, @CuriousLearner, @corona10, @lysnikolaou, @nitishch, @pablogsal, @miss-islington, @tirkarthi, @piyushhajare, @brandtbucher, @iritkatriel
PRs
  • bpo-34013: Generalize the invalid legacy statement error message #27389
  • bpo-34013: Move migration suggestion for print expr to parser #27390
  • [3.10] bpo-34013: Generalize the invalid legacy statement error message (GH-27389). #27391
  • bpo-34013: Move the Python 2 hints from the exception constructor to the parser #27392
  • [3.10] bpo-34013: Move the Python 2 hints from the exception constructor to the parser (GH-27392) #27393
  • bpo-34013: Don't consider a grouped expression when reporting legacy print syntax errors #27521
  • [3.10] bpo-34013: Don't consider a grouped expression when reporting legacy print syntax errors (GH-27521) #27522
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2021-08-01.01:11:49.890>
    created_at = <Date 2018-06-30.17:42:58.028>
    labels = ['interpreter-core', 'type-bug', '3.10', 'release-blocker']
    title = 'Inconsistent SyntaxError for print'
    updated_at = <Date 2021-08-10.19:18:12.540>
    user = 'https://github.com/corona10'

    bugs.python.org fields:

    activity = <Date 2021-08-10.19:18:12.540>
    actor = 'terry.reedy'
    assignee = 'none'
    closed = True
    closed_date = <Date 2021-08-01.01:11:49.890>
    closer = 'pablogsal'
    components = ['Parser']
    creation = <Date 2018-06-30.17:42:58.028>
    creator = 'corona10'
    dependencies = []
    files = []
    hgrepos = []
    issue_num = 34013
    keywords = ['patch']
    message_count = 33.0
    messages = ['320797', '320824', '320828', '320834', '320836', '320838', '320839', '320840', '320851', '321196', '321212', '387153', '387155', '387158', '387289', '387290', '387291', '387292', '387307', '387313', '387317', '398267', '398307', '398312', '398316', '398318', '398652', '398671', '398673', '398674', '399317', '399323', '399354']
    nosy_count = 17.0
    nosy_names = ['terry.reedy', 'ncoghlan', 'eric.smith', 'aroberge', 'serhiy.storchaka', 'veky', 'ammar2', 'CuriousLearner', 'corona10', 'lys.nikolaou', 'nitishch', 'pablogsal', 'miss-islington', 'xtreak', 'piyushhajare', 'brandtbucher', 'iritkatriel']
    pr_nums = ['27389', '27390', '27391', '27392', '27393', '27521', '27522']
    priority = 'release blocker'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'behavior'
    url = 'https://bugs.python.org/issue34013'
    versions = ['Python 3.10']

    @corona10
    Copy link
    Member Author

    Python 3.8.0a0 (heads/master-dirty:0cdf5f4289, Jul  1 2018, 02:30:31)
    [Clang 9.1.0 (clang-902.0.39.1)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print "hi"
      File "<stdin>", line 1
        print "hi"
                 ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print("hi")?
    >>> def add(a,b):
    ...     return a + b
    ...
    >>> print add(3,5)
      File "<stdin>", line 1
        print add(3,5)
                ^
    SyntaxError: invalid syntax

    IMHO, an error message should be 'SyntaxError: Missing parentheses in call to 'print'. Did you mean print(add(3,5))?' but it is not.

    @corona10 corona10 added 3.8 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) type-feature A feature request or enhancement labels Jun 30, 2018
    @tirkarthi
    Copy link
    Member

    Related issue that introduced the error message if you would like to take a look at the implementation : https://bugs.python.org/issue30597

    Thanks

    @corona10
    Copy link
    Member Author

    corona10 commented Jul 1, 2018

    @XTreak

    Thanks, I have interest with this issue :)
    I will take a look at the implementation

    Thanks!

    @piyushhajare
    Copy link
    Mannequin

    piyushhajare mannequin commented Jul 1, 2018

    I'm interested to solve this issue

    @tirkarthi
    Copy link
    Member

    I took an initial stab at this and there is a comment where if there is an open paren then to use the normal error message. Additional context on the issue which lists false positive cases where the change was introduced https://bugs.python.org/issue21669. I removed the check where the left paren count is not -1 in case of a function call and it seems to work. Of course, there are cases to handle as mentioned in the linked issue and I tried some of the cases like "foo".toupper().tolower() and the results as below :

    Patch :

    diff --git a/Objects/exceptions.c b/Objects/exceptions.c
    index bb50c1c..7e616cf 100644
    --- a/Objects/exceptions.c
    +++ b/Objects/exceptions.c
    @@ -2966,10 +2966,7 @@ _report_missing_parentheses(PySyntaxErrorObject *self)
         if (left_paren_index < -1) {
             return -1;
         }
    -    if (left_paren_index != -1) {
    -        /* Use default error message for any line with an opening paren */
    -        return 0;
    -    }
    +
         /* Handle the simple statement case */
         legacy_check_result = _check_for_legacy_statements(self, 0);
         if (legacy_check_result < 0) {

    Cases :

    ➜ cpython git:(master) ✗ cat foo_print.py
    class Foo:
    pass

    print Foo().header(a=foo(1))
    ➜ cpython git:(master) ✗ ./python foo_print.py
    File "foo_print.py", line 4
    print Foo().header(a=foo(1))
    ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(Foo().header(a=foo(1)))?

    ➜ cpython git:(master) ✗ cat foo_print.py
    print "a".toupper().tolower()
    ➜ cpython git:(master) ✗ ./python foo_print.py
    File "foo_print.py", line 1
    print "a".toupper().tolower()
    ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print("a".toupper().tolower())?

    Linked cases in the patch :

    ➜ cpython git:(master) ✗ cat foo_print.py
    print (foo.)
    ➜ cpython git:(master) ✗ ./python foo_print.py
    File "foo_print.py", line 1
    print (foo.)
    ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print((foo.))?

    Would like to link to https://hackmd.io/s/ByMHBMjFe#08-Debugging-Python-Objects . As a beginner the tutorial was helpful and I had to set a break point on Objects/exceptions.c:1323 where SyntaxError_init is called and then execute line by line where the current code returns the left_paren_index which you can set as set left_paren_index = -1 to skip the branch and then continue the program to print the above error messages. IMO I think it was an explicit decision with this change being done 4 years ago with the false positive cases and tests listed but I am curious if there are any improvements in this area to be done.

    Thanks

    @corona10
    Copy link
    Member Author

    corona10 commented Jul 1, 2018

    Thanks,

    my easy patch is worked by pre-checking left paren index. but some edge case which I don't expect should be considered.

         if (left_paren_index != -1) {
    -        /* Use default error message for any line with an opening paren */
    -        return 0;
    +	int found_white_space = 0;
    +	int found_other_char = 0;
    +	Py_ssize_t pos = 0;
    +	while(pos != left_paren_index) {
    +	    int kind = PyUnicode_KIND(self->text);
    +	    void *data = PyUnicode_DATA(self->text);
    +            Py_UCS4 ch = PyUnicode_READ(kind, data, pos);
    +	    if (Py_UNICODE_ISSPACE(ch)) {
    +                found_white_space = 1;
    +	    }
    +	    if (!Py_UNICODE_ISSPACE(ch) && found_white_space == 1) {
    +                found_other_char = 1;
    +	    }
    +	    pos++;
    +	}
    +
    +	if (found_other_char == 0) {
    +	    return 0;
    +	}
         }
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print (foo.)
      File "<stdin>", line 1
        print (foo.)
                   ^
    SyntaxError: invalid syntax
    
    >>> print "a".toupper().tolower()
      File "<stdin>", line 1
        print "a".toupper().tolower()
                ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print("a".toupper().tolower())?

    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(Foo().header(a=foo(1)))?

    @corona10
    Copy link
    Member Author

    corona10 commented Jul 1, 2018

    >>> class Foo:
    ...     pass
    ...
    >>> print Foo().header(a=foo(1))
      File "<stdin>", line 1
        print Foo().header(a=foo(1))
                ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(Foo().header(a=foo(1)))?

    @corona10
    Copy link
    Member Author

    corona10 commented Jul 1, 2018

    And here's my work in progress patch.

    corona10@1338253

    @tirkarthi
    Copy link
    Member

    @corona10 You might want to discuss this at https://mail.python.org/pipermail/python-ideas/ so that you get some initial feedback on the work and it's acceptance to the core.

    Thanks

    @terryjreedy
    Copy link
    Member

    Eric, Nick, Serhiy: this issue proposes to extend the print message patch of bpo-30597 to cover more cases. On the face of it, this seems sensible, but I have no read the original discussion or the current and proposed patches.

    @serhiy-storchaka
    Copy link
    Member

    See also bpo-32685.

    @iritkatriel
    Copy link
    Member

    Still the same in 3.10:

    Python 3.10.0a5+ (heads/master:bf2e7e55d7, Feb 11 2021, 23:09:25) [MSC v.1928 64 bit (AMD64)] on win32
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print 3
      File "<stdin>", line 1
        print 3
              ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(3)?
    >>> def f(x): return x
    ...
    >>> print f(3)
      File "<stdin>", line 1
        print f(3)
              ^
    SyntaxError: invalid syntax

    @iritkatriel iritkatriel added 3.10 only security fixes and removed 3.8 only security fixes labels Feb 17, 2021
    @serhiy-storchaka
    Copy link
    Member

    Would it be too much if add a Python 2 rule for print statement in grammar to produce better error message?

    invalid_print_stmt:
    | 'print' ( test (',' test)* [','] ] |
    '>>' test [ (',' test)+ [','] ) {
    RAISE_INVALID_PRINT_STATEMENT() }

    @pablogsal
    Copy link
    Member

    Would it be too much if add a Python 2 rule for print statement in grammar to produce better error message?

    Please, go ahead. I think it makes sense and with our latest change in the parser, such new error message will have no impact on performance whatsoever. Please, at me as a reviewer :)

    @vedgar
    Copy link
    Mannequin

    vedgar mannequin commented Feb 19, 2021

    Aren't we overthinking this? Python 2 is a dead language. It has reached end of life more than a year ago (and was scheduled to do so in 2015). Why are we still trying to accomodate something that stopped being relevant a long time ago?

    @ammaraskar
    Copy link
    Member

    It's still one of the most common beginner mistakes, personally I think the trade-off in complexity at least for the grammar level fix is worth it here.

    @terryjreedy
    Copy link
    Member

    'Consistency' is in the eye of the beholder in that it is relative to some ideal. 'Inconsistent' has too much baggage as bad'. I would prefer to call the current rule 'restricted' or 'limited' and judge any expansion on its own merits.

    The arguments to print can take an unbounded amount of space. So a general message "Did you mean print(<full argument list>)?" could also be indefinitely long. I don't think this is a good idea. Of course, the same applies to a single literal, especially multiline string literals. But at least the latter are currently clipped to only the first line in the message.

    >>> print '''first line
    second line'''
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print('''first line)?

    (The above would be better with trailing ... .) This abbreviation would be harder with multiple arguments. The special message is for beginners. They might print a literal or name without ()s more frequently. I not sure that they need the reminder with every mistake.

    @iritkatriel
    Copy link
    Member

    I agree with Terry’s point about printing long expressions in the error msg. Perhaps just the preamble would be useful though:

    SyntaxError: Missing parentheses in call to 'print'.

    Instead of just

    SyntaxError: invalid syntax.

    @vedgar
    Copy link
    Mannequin

    vedgar mannequin commented Feb 19, 2021

    "It's still one of the most common beginner mistakes"

    Do you have any data to back this up? I really think it's overblown.

    On the other hand, if it really _is_ so, how about changing the language? It wouldn't be the first thing that was changed for Py3, and then changed back once people realized the old way was better.

    It seems to me totally backwards to have a construct in the grammar, only to report it as an error. "I understand you, but you still have to use _this_ way to write what you want." I really think Python shouldn't be that kind of language.

    @pablogsal
    Copy link
    Member

    Let's step back a bit and focus on the issue at hand. The problem is the following:

    • We **already** have a warning for the print statement without parens:
    Python 3.9.1 (default, Dec 14 2020, 11:49:16)
    [Clang 12.0.0 (clang-1200.0.32.27)] on darwin
    Type "help", "copyright", "credits" or "license" for more information.
    >>> print x
      File "<stdin>", line 1
        print x
              ^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(x)?

    This is achieved by inspecting the syntax error and checking some conditions, which I personally find it uglier than a resilient grammar rule.

    • The question is if we want to make the rule more resilient or delete it whatsoever. The status quo doesn't seem like a good fit

    @aroberge
    Copy link
    Mannequin

    aroberge mannequin commented Feb 19, 2021

    +1 to the idea of adding something to the grammar, and have a simple error message:

    SyntaxError: Missing parentheses in call to 'print'.

    in *all* cases, including the first one that prompted this bug report.

    I write that even though I have created a third-party package based on the idea that beginners (and sometimes others...) might benefit from a more detailed error message (which is taken care of already by friendly-traceback).

    @iritkatriel
    Copy link
    Member

    This case has changed recently, and not for the better:

    >>> print f(3)
      File "<stdin>", line 1
        print f(3)
        ^^^^^^^^^^
    SyntaxError: invalid syntax. Perhaps you forgot a comma?

    @pablogsal
    Copy link
    Member

    New changeset 6948964 by Pablo Galindo Salgado in branch 'main':
    bpo-34013: Generalize the invalid legacy statement error message (GH-27389)
    6948964

    @pablogsal
    Copy link
    Member

    New changeset b977f85 by Pablo Galindo Salgado in branch '3.10':
    [3.10] bpo-34013: Generalize the invalid legacy statement error message (GH-27389). (GH-27391)
    b977f85

    @pablogsal
    Copy link
    Member

    New changeset ecc3c8e by Pablo Galindo Salgado in branch 'main':
    bpo-34013: Move the Python 2 hints from the exception constructor to the parser (GH-27392)
    ecc3c8e

    @miss-islington
    Copy link
    Contributor

    New changeset 68e3dca by Miss Islington (bot) in branch '3.10':
    bpo-34013: Move the Python 2 hints from the exception constructor to the parser (GH-27392)
    68e3dca

    @brandtbucher
    Copy link
    Member

    Reopening as a release blocker.

    It appears that this new rule is far too eager, and matches a much wider range of inputs than intended. Here is a case where it changes an IndentationError into a SyntaxError:

    $ cat bug.py
    print()
        boom

    On 3.9 (correct):

    $ ./python.exe --version
    Python 3.9.6+
    $ ./python.exe bug.py
      File "/Users/brandtbucher/Desktop/GitHub/cpython/bug.py", line 2
        boom
    IndentationError: unexpected indent

    On 3.10 (incorrect):

    $ ./python.exe --version
    Python 3.10.0b4+
    $ ./python.exe bug.py
      File "/Users/brandtbucher/Desktop/GitHub/cpython/bug.py", line 1
        print()
        ^^^^^^^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

    On 3.11 (incorrect):

    $ ./python.exe --version
    Python 3.11.0a0
    $ ./python.exe bug.py
      File "/Users/brandtbucher/Desktop/GitHub/cpython/bug.py", line 1
        print()
        ^^^^^^^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

    I recommend that this either be fixed or reverted before the RC.

    @brandtbucher brandtbucher added interpreter-core (Objects, Python, Grammar, and Parser dirs) and removed interpreter-core (Objects, Python, Grammar, and Parser dirs) labels Jul 31, 2021
    @brandtbucher brandtbucher reopened this Jul 31, 2021
    @brandtbucher brandtbucher added release-blocker type-bug An unexpected behavior, bug, or error and removed type-feature A feature request or enhancement labels Jul 31, 2021
    @pablogsal
    Copy link
    Member

    Is not about the eagerness, the problem is that it matches *first*, the parser never gets to the indentation error in the second phase.

    For example, with:

    print(3) $ 34

    ❯ ./python bug.py
    File "/home/pablogsal/github/python/main/bug.py", line 1
    print(3) $ 34
    ^^^^^^^
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

    The problem is that is matching the (3) as print + a number between parentheses. We just need to disallow to continue matching on the right
    if it finds a '('.

    @pablogsal
    Copy link
    Member

    New changeset 208a7e9 by Pablo Galindo Salgado in branch 'main':
    bpo-34013: Don't consider a grouped expression when reporting legacy print syntax errors (GH-27521)
    208a7e9

    @miss-islington
    Copy link
    Contributor

    New changeset 35035bc by Miss Islington (bot) in branch '3.10':
    bpo-34013: Don't consider a grouped expression when reporting legacy print syntax errors (GH-27521)
    35035bc

    @aroberge
    Copy link
    Mannequin

    aroberge mannequin commented Aug 10, 2021

    Python 3.10.0rc1 ...

    >>> print hello world!
      File "<stdin>", line 1
        print hello world!
              ^^^^^^^^^^^
    SyntaxError: invalid syntax. Perhaps you forgot a comma?

    The hint given is not exactly helpful ...

    (This example was in a discussion on Twitter https://twitter.com/cfbolz/status/1425036431974715400 about a previous handling of this invalid syntax case where it was mentioned that pypy verifies that the suggestion it makes actually yields syntactically valid code.)

    @pablogsal
    Copy link
    Member

    If we change the priority of the error messages, which is unclear if is easily possible, the error will suggest that

    print hello should be parenthesized as print (hello) Which if corrected will leave the "world" part out as a call followed to a name, and here the comma suggestion is a valid concern.

    I understand the willingness to have a better error here, but I am not sure what we can improve here.

    @terryjreedy
    Copy link
    Member

    I think that this was properly closed after the last fix. There are multiple issues at play:

    1. These parts of the Zen:
      "Special cases aren't special enough to break the rules.
      Although practicality beats purity.
      ...
      In the face of ambiguity, refuse the temptation to guess."

    2. The fact that a parser only reads and checks *python* syntax, while we humans have to make a special effort to avoid semantics. The latter is especially true when the semantic is a special-case computing trope. Even if an identifier is semantically recognized as a function, its signature (call syntax) is specific to the function and is not part of python syntax.

    3. The that with PEG parsing, there is no exact failure point. (Pablo just documented the PEG parser. But, Pablo, where is it? Not in HOWTO or index.) This is especially true when there are multiple errors, as in Andre's example.

    As long as we only had one special case hint, which depended on the semantic knowledge that 'print' is almost certainly meant to mean print with the builtin function with a known call syntax, it was fairly easy to get the hint right, and in 3.11 we still have

    >>> print """jsfl sf
    ... sjflsfj l
    ... sjflsjf
    ... sjfs fj
    ... sjlsfjlsjg
    ... """
    SyntaxError: Missing parentheses in call to 'print'. Did you mean print(...)?

    But we now have more hints, and "print Hello world!" is syntactically the same as "a b c!", which gives the same error message. Even if the first identifier is recognized as a function, and parenthesis are added, the result would be syntactically identical to "print(b c!)". At this point, it is syntactically ambiguous whether the fixed argument should be a string "'b c!'" or comma list "b, c". Both are possible for print, but not all functions. The 'correct' fix on only 'obvious' when we add knowledge of the semantics 'Hello, 'world', and '!' and the history of their concatenation, 'Hello world!' (but sometimes without '!' or capitalization, or with <name> replacing 'world'), in computing, and especially in the teaching of beginners in a particular language.

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    3.10 only security fixes interpreter-core (Objects, Python, Grammar, and Parser dirs) release-blocker type-bug An unexpected behavior, bug, or error
    Projects
    None yet
    Development

    No branches or pull requests

    9 participants