These all match my expectations. Some glosses: [Guido]
So the way I envision it is that *in the absence of a nonlocal or global declaration in the containing scope*, := inside a comprehension or genexpr causes the compiler to assign to a local in the containing scope, which is elevated to a cell (if it isn't already). If there is an explicit nonlocal or global declaration in the containing scope, that is honored.
If the genexp/listcomp is at module level, then "assign to a local in the containing scope" still makes sense ("locals" and "globals" mean the same thing at module level), but "elevated to a cell" doesn't then - it's just a plain global. In absolutely all cases, what I expect is that NAME := EXPR in a genexp/listcomp do the binding _as if_ NAME = object_EXPR_evaluates_to were executed in the immediately containing scope. Describing the goal instead of part of the implementation may be easier to grasp ;-)
Examples:
# Simplest case, neither nonlocal nor global declaration def foo(): [p := q for q in range(10)] # Creates foo-local variable p print(p) # Prints 9
# There's a nonlocal declaration def bar(): p = 42 # Needed to determine its scope def inner(): nonlocal p [p := q for q in range(10)] # Assigns to p in bar's scope inner() print(p) # Prints 9
# There's a global declaration def baz(): global p [p := q for q in range(10)] baz() print(p) # Prints 9
All these would work the same way if you wrote list(p := q for q in range(10)) instead of the comprehension.
100% agreed. Add at module scope: [p := q for q in range(10)] print(p) # Prints 9 But uou're on your own for class scope, because I never did anything fancy enough at class scope to need to learn how it works ;-)
We should probably define what happens when you write [p := p for p in range(10)]. I propose that this overwrites the loop control variable rather than creating a second p in the containing scope -- either way it's probably a typo anyway.
A compile-time error would be fine by me too. Creating two meanings for `p` is nuts - pick one in case of conflict. I suggested before that the first person with a real use case for this silliness should get the meaning their use case needs, but nobody bit, so "it's local then" is fine.
:= outside a comprehension/genexpr is treated just like any other assignment (other than in-place assignment), i.e. it creates a local unless a nonlocal or global declaration exists.
Also agreed. People have total control over scopes in explicitly given functions now, and if the compiler magically made anything nonlocal they would have no way to stop it. Well, I suppose we could add a "non_nonlocal" declaration, but I'd rather not ;-)