-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
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
Support PEP 572 #6899
Support PEP 572 #6899
Conversation
test-data/unit/check-python38.test
Outdated
pass | ||
|
||
# Just make sure we don't crash on this sort of thing. | ||
if (NT := NamedTuple("NT", [("x", int)])): # E: "int" not callable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is probably because we define special forms as NamedTuple = 0
in the stub. If we think users are likely to actually try this, I can work on a better error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, this is one of biggest new features in Python 3.8! The implementation looks solid -- I mostly left some minor comments.
I have a few bigger comments:
- I wonder if assignment expression should have special logic in
mypy.server.deps
, similar to assignment statements? This can happen in a separate PR, or just create a follow-up issue (if relevant). - This may interact with
--allow-redefinition
(mypy.renaming
). Again, you can create a follow-up issue or PR if you this that this might be the case.
mypy/newsemanal/semanal.py
Outdated
"""Analyze an lvalue or assignment target. | ||
|
||
Args: | ||
lval: The target lvalue | ||
nested: If true, the lvalue is within a tuple or list lvalue expression | ||
explicit_type: Assignment has type annotation | ||
escape_comprehensions: If we are inside a comprehension, set the variable | ||
in the enclosing scope instead. This implements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: indent the second and following lines in an argument specification like this:
"""
...
Args:
argument: Something something ...
explanation continues here ...
mypy/semanal.py
Outdated
@@ -2107,9 +2116,15 @@ def analyze_lvalue(self, lval: Lvalue, nested: bool = False, | |||
nested: If true, the lvalue is within a tuple or list lvalue expression | |||
add_global: Add name to globals table only if this is true (used in first pass) | |||
explicit_type: Assignment has type annotation | |||
escape_comprehension: If we are inside a comprehension, set the variable | |||
in the enclosing scope instead. This implements |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Style nit: indent second and third lines, similar to above.
test-data/unit/check-python38.test
Outdated
[case testWalrus] | ||
from typing import NamedTuple | ||
|
||
if (a := 2): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that the parentheses are optional here. Maybe leave out all optional parentheses?
test-data/unit/check-python38.test
Outdated
|
||
[builtins fixtures/f_string.pyi] | ||
|
||
[case testWalrusNewSemanal] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All the tests are also ran with the new semantic analyzer in CI, so duplicating the test case is probably not necessary. If there are some differences between the old and the new semantic analyzer, it's better to isolate the differences into additional test cases and share most of the test case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't realize that we run all tests with the new semanal. I'll remove the duplicate test case.
test-data/unit/check-python38.test
Outdated
if (Alias := int): | ||
z3: Alias # E: Invalid type "Alias" | ||
|
||
return (y8 := 3) + y8 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this resusing the name y8
from the lambda above by design? What about testing whether a variable defined inside a lambda using :=
escapes to the outer scope?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point; I'll use a different variable name here and check that the one from the lambda doesn't escape.
Thanks for the review! I'll address your comments soon. For the two bigger issues you flag, I think it's better to handle them in separate PRs. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! I have two additional comments from a very brief review.
@@ -2421,6 +2421,11 @@ def check_list_multiply(self, e: OpExpr) -> Type: | |||
e.method_type = method_type | |||
return result | |||
|
|||
def visit_assignment_expr(self, e: AssignmentExpr) -> Type: | |||
value = self.accept(e.value) | |||
self.chk.check_assignment(e.target, e.value) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to me this direct call will result in allowing assignments to final variables.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I'll add self.chk.check_final
.
|
||
reveal_type(c) # E: Revealed type is 'builtins.int' | ||
|
||
[builtins fixtures/f_string.pyi] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would like to see more checker related tests here, namely:
- Interactions with final names/attributes (see the comment above)
- Interactions with binder (this will probably work as is, but without a test factoring out binder code from
check_assignment()
to containing method will cause the same problem as withFinal
now) - Interactions with partial types and deferred nodes (mostly for the same reason as above)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Adding this now. I'm omitting final attributes because only names are allowed in the LHS of :=.
$ python -c 'if self.x := 2: pass'
File "<string>", line 1
SyntaxError: cannot use named assignment with attribute
Also, what would be a good test for a deferred node? Perhaps a variable assigned in an assignment expression with a type that is only defined later in the file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps a variable assigned in an assignment expression with a type that is only defined later in the file?
Yes, r.h.s. can be an inferred attribute of some class that is initialized with a function call.
Submitted mypyc/mypyc#637
test-data/unit/check-python38.test
Outdated
reveal_type(x) # E: Revealed type is 'builtins.int' | ||
|
||
if x or (y := 1): | ||
reveal_type(y) # E: Revealed type is 'Union[builtins.int, None]' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe also add a test that the same if
where or
is replaced with and
will result in y
being int
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, it actually infers the type as Optional[int] if I do that. I'll take a deeper look.
def check_partial() -> None: | ||
x = None | ||
if bool() and (x := 2): | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe add a similar test where you first assign (x := [])
, and then append something to x
.
What is the status here? Is there a chance this will be ready by the end of the week? |
I realized your request in #6899 (comment) is somewhat hard to resolve. Would it be OK to handle that in a followup PR? |
Not sure how my branch affects this
This reverts commit d10efb5.
If it is tricky then OK. @JukkaL will you have time for a second round of review this week? If not I will review this on the weekend. |
OK, tests pass again now. Known issues that aren't covered:
I'll create followups for these if this PR is approved. |
Thank you! I don't think I can review this (my mypy internals are too rusty), but I tried this on some real code I've written recently, and stumbled upon an issue. from typing import Optional
class C:
def foo(self):
pass
def good(b: Optional[C]) -> None:
a = b
if a:
a.foo()
def bad(b: Optional[C]) -> None:
if a := b:
a.foo() # E: Item "None" of "Optional[C]" has no attribute "foo" Since the two functions are equivalent, the second should not get that error. |
Oh I guess that's your first bullet... |
No, that's separate from the issue I was aware of, since there's no |
I found a PR for mypy that supports the walrus operator: python/mypy#6899 (This fixes some of the more egregious errors, but there are about 50 more.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to get this moving, @JelleZijlstra could you please merge this PR and continue addressing issues in follow-up PRs? (You can create an issue with a list of remaining problems.)
There is a huge merge conflict but I think it can be resolved by just copying changed parts to the moved new analyzer which is now the only one.
Yes, sorry for not getting around to fix the other remaining issues. I'll fix the merge conflict and merge this PR over the next few days, and open issues for the remaining problems. (I have a feeling we'll continue to run into PEP 572/binder edge cases for a while, so it would make sense to have multiple issues we can organize under a topic.) |
A couple of tricky aspects: