Short version of my more detailed answer below:

While my rationale is different from Barry's, I've convinced myself
that the right thing to do for Python 3.8 is to remove the new
TargetScopeError and have these cases all just raise SyntaxError.

I do still see potential value in a new "AssignmentScopeError"
subclass, but it would apply to more than just these new exceptions,
and can be considered on a more leisurely basis for Python 3.9.

On Fri, 9 Aug 2019 at 03:07, Barry Warsaw <ba...@python.org> wrote:
>
> bpo-37757: https://bugs.python.org/issue37757
>
> This issue describes some corner cases in PEP 572 (assignment expressions) 
> syntax that haven’t been implemented yet.  The PEP currently says:
>
> "The two invalid cases listed above raise TargetScopeError, a new subclass of 
> SyntaxError (with the same signature).”
>
> The PEP doesn’t really go into the rationale for why a new exception is being 
> defined, and in the issue I’ve argued that we should just raise SyntaxError 
> in those cases.  To me, “TargetScopeError” is pretty obscure and doesn’t give 
> users an obvious clue as to what’s going wrong, without searching the 
> interwebs.
>
> Nick argues (apologies if I’m misrepresenting you!):
>
> "I believe our main motivation for separating it out was the fact that even 
> though TargetScopeError is a compile-time error, the affected code is 
> syntactically fine - there are just issues with unambiguously inferring the 
> intended read/write location for the affected target names. (Subclassing 
> SyntaxError is then a pragmatic concession to the fact that "SyntaxError" 
> also de facto means "CompilationError”)”

This accurately conveys what I wrote on the PR and issue, but what I
wrote there didn't capture the full background for when
TargetScopeError gets raised.

The main motivating case that led to the new exception being defined
is the one that was already implemented as part of the initial PR
adding assignment expressions: the fact we can't readily make PEP
572's parent local scoping for comprehensions work when that parent
scope is a class scope. Hence, the TargetScopeError raised in this
case:

    >>> class C:
    ...     [x := 10 for i in range(5)]
    ...
     File "<stdin>", line 2
    TargetScopeError: named expression within a comprehension cannot
be used in a class body

Syntactically, the individual pieces of this code are perfectly fine -
the problem is that the calculated target for "x" would be in the
class body, and there isn't currently a way for the compiler to make
that name accessible both in the class scope and in the comprehension
body. (We're only able to make zero-arg super work because we don't
need to make `__class__` accessible while the class is executing).

The new PR then adds the additional checks to disallow the cases where
we don't want to enshrine "What CPython's current implementation
handles this code" as "The way this code must behave in all Python
implementations":

    >>> [i := 10 for i in range(5)]
      File "<stdin>", line 1
    TargetScopeError: named expression cannot rebind comprehension
iteration variable

    >>> [True for i in range(5) if (j := True) for j in range(5)]
      File "<stdin>", line 1
    TargetScopeError: comprehension inner loop cannot rebind named
expression target

    >>> [True for i in (iterable := range(5))]
      File "<stdin>", line 1
    TargetScopeError: named expression cannot be used in comprehension
iterable expression

I agree that it's a problem that "Assignment" doesn't appear anywhere
in the exception name - if we're going to have a specific name for
this, then something like "AssignmentScopeError" would be better.

The main counterargument to this approach would be that other similar
scoping issues currently just raise SyntaxError:

    >>> def f(x): global x
    ...
     File "<stdin>", line 1
    SyntaxError: name 'x' is parameter and global
    >>> def f(x): nonlocal x
    ...
     File "<stdin>", line 1
    SyntaxError: name 'x' is parameter and nonlocal
    >>> def f(): global x; nonlocal x
    ...
     File "<stdin>", line 1
    SyntaxError: name 'x' is nonlocal and global
    >>> def f(): nonlocal x
       ...
     File "<stdin>", line 1
    SyntaxError: no binding for nonlocal 'x' found

(Those also highlight that I should tweak the new name specific
binding conflict errors to mention the variable name)

With a revised exception name though, I think even those existing
errors would be improved by switching them over to the subclass:

    >>> def f(x): global x
    ...
     File "<stdin>", line 1
    AssignmentScopeError: name 'x' is parameter and global
    >>> def f(x): nonlocal x
    ...
     File "<stdin>", line 1
    AssignmentScopeError: name 'x' is parameter and nonlocal
    >>> def f(): global x; nonlocal x
    ...
     File "<stdin>", line 1
    AssignmentScopeError: name 'x' is nonlocal and global
    >>> def f(): nonlocal x
       ...
     File "<stdin>", line 1
    AssignmentScopeError: no binding for nonlocal 'x' found

However, a change like that would make the most sense when considered
independently of the specific case of assignment expressions -
instead, we would want to ask "What kinds of exceptions does the
symbol table analysis pass raise?", and then consider whether it might
make sense to define subclasses of syntax error for them.

Given the naming issue, and the fact that a potential new SyntaxError
subclass would be relevant for more than just the new PEP 572
exceptions, I think the right near term approach is to go with the
generic SyntaxError as Barry suggests. I'll update my PRs accordingly.

Cheers,
Nick.

-- 
Nick Coghlan   |   ncogh...@gmail.com   |   Brisbane, Australia
_______________________________________________
Python-Dev mailing list -- python-dev@python.org
To unsubscribe send an email to python-dev-le...@python.org
https://mail.python.org/mailman3/lists/python-dev.python.org/
Message archived at 
https://mail.python.org/archives/list/python-dev@python.org/message/456A2E355ENIMU4742AP25BH62IQQ77I/

Reply via email to