Better SyntaxError hints for 'key: value' pairs with wrong delimiters

Since 3.10, the parser provides excellent targeted hints for dict literal mistakes:

>>> {1: 2, 3: 4, 5}
SyntaxError: ':' expected after dictionary key

>>> {1: 2, 3: 4, 5:}
SyntaxError: expression expected after dictionary key and ':'

>>> {1: *a}
SyntaxError: cannot use a starred expression in a dictionary value

However, using the wrong delimiters for ‘key: value’ pairs still produces a bare invalid syntax.

In my experience, this is a common error for beginners: sometimes as a typo, sometimes because they’re not used to differentiating between tuples, lists and dicts. And a bare invalid syntax doesn’t help them figure out what’s wrong.

On 3.14.6 these all give a bare invalid syntax, and invalid.gram in main has no rules for them:

>>> x = ["a": 1, "b": 2]
SyntaxError: invalid syntax

>>> x = ("a": 1, "b": 2)
SyntaxError: invalid syntax

>>> x = [k: v for k, v in pairs]
SyntaxError: invalid syntax

>>> x = (k: v for k, v in pairs)
SyntaxError: invalid syntax

>>> f(a: 1)
SyntaxError: invalid syntax

My proposal is to add rules to provide better hints:

>>> x = ["a": 1, "b": 2]
SyntaxError: invalid syntax. Did you mean to use '{' and '}' instead of '[' and ']' to create a dictionary?

>>> x = ("a": 1, "b": 2)
SyntaxError: invalid syntax. Did you mean to use '{' and '}' instead of '(' and ')' to create a dictionary?

>>> x = [k: v for k, v in pairs] # or (k: v ...)
SyntaxError: invalid syntax. Did you mean to use '{' and '}' instead of '[' and ']' for a dict comprehension?

>>> f(a: 1)
SyntaxError: invalid syntax. Did you mean to use '=' instead of ':' to pass a keyword argument in a function call?

I’m less sure about the last one, though. I think it’s much more likely that someone is making a function call and mixing up = with : than trying to slice a dict with ( instead of [. So the hint only suggests the keyword argument, since mentioning a dict could end up making things more confusing.


To avoid false positives, the new rules must account for valid uses of colons inside brackets or parentheses:

  • Slicing: d["a": 1] is valid. The rule must apply only to list displays, never to subscripts.
  • Annotated assignments: (a): int = 1 is valid. The parentheses rule must apply only in expression contexts.

I couldn’t find a similar previous proposal. Would this be welcome? I’d be happy to open an issue and work on a PR.

3 Likes

This sounds great to me! The error descriptions are rather verbose; I’d suggest not repeating what the user has already done as much to help, so they’re easier for the reader to parse.

Existing errors on 3.14 appear as:

>>> {1: 2, 3: 4, 5}
  File "<python-input-0>", line 1
    {1: 2, 3: 4, 5}
                 ^
SyntaxError: ':' expected after dictionary key

You can use this technique to point to the issue rather than explain it:

>>> x = ["a": 1, "b": 2]
  File "<python-input-0>", line 1
    x = ["a": 1, "b": 2]
        ^
SyntaxError: invalid syntax. Did you mean to use '{' and '}' to create a dictionary?

If you could point to both the opening and closing brackets that would be ideal, but I think the parser only currently allows ranges, not distinct error locations.

I’m rather skeptical about the amount of gueswork what the user really wanted when the type of parens does not match the inner expression. I don’t think it’s always obvious which part is wrong. The error message should just reflect that IMO. Something like Expression inside brackets [ and ] does not form a valid list. Are you sure you want a list? - with better wording by a native speaker, of course.

Agreed. Consider that while x[1:1] is a perfectly valid slice of x. If you accidentally hit the = key and got x=[1:1], you didn’t mean to create a dictionary, and the hint is probably more confusing than helpful.

Also, maybe the user is aware of slice notation and thought it would be nice to create a list or tuple of slices? That’s not a beginner mistake, but it’s not an unreasonable mistake for someone more experienced to make (I only discovered you can’t do that while experimenting as part of writing this post!)

I like the idea in principle, but I don’t think it’s as obvious what the developer’s intent was as the OP hopes.