-
-
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
[conflict] Fix #1855: Multiassign from Union #2154
Conversation
if inferred: | ||
self.set_inferred_type(inferred, lv, union) | ||
else: | ||
self.store_type(lv, union) |
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.
The binding of the joined type is "retrofitted". I am not sure this is the best way to do it - maybe inferred vars should simply accumulate through joins?
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.
Retrofitting like this may not be a problem, but I suspect it might be. Accumulating through joins might not be any better. The most correct approach would be to infer the entire type for an lvalue before setting it, so that intermediate values can't leak. I don't know, but it may be possible that the intermediate lvalue types leak to the rvalue in some weird corner cases.
I'd recommend not worrying about this too much, but I'd suggest adding a TODO comment about the potential issues.
Only very briefly (perhaps I will get to it shortly). From a first glance it seems unrelated. I don't have the right overloads for int+float in the builtin fixture, but perhaps I can manage without it. |
Hm, there's automatic promotion from int->float as needed in mypy, so the overloads shouldn't be needed; also the |
i = None # type: int
f = None # type: float
i + f I get |
@elazarg You'll likely need overloads in your builtins fixture for that to work in a test case. |
Thanks, I will work on that. Again, it is unrelated to this PR. |
def check_multi_assign(self, lvalues: List[Expression], | ||
rvalue: Expression, rvalue_type: Type, | ||
context: Context, infer_lvalue_type: bool = True, | ||
undefined_rvalue = False) -> 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.
No annotation for undefined_rvalue
.
infer_lvalue_type) | ||
else: | ||
self.msg.type_not_iterable(rvalue_type, context) | ||
def type_is_iterable(self, rvalue_type: Type) -> bool: |
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'd rather not rename the argument to rvalue_type
, as this method works for any types, even if it's only used for rvalues right now
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.
Oops that's weird. I have no idea what was I thinking.
from typing import Union, Tuple, Any | ||
|
||
a = None # type: Tuple[int] | ||
(a1,) = a |
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.
Reveal type of a1
.
(a1,) = a | ||
|
||
b = None # type: Union[Tuple[int], Tuple[float]] | ||
(b1,) = b |
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.
Reveal type of b1
.
from typing import Union, Tuple, Any | ||
|
||
d = None # type: Union[Any, Tuple[float, float]] | ||
(d1, d2) = d |
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.
Reveal types of d1
and d2
.
(d1, d2) = d | ||
|
||
e = None # type: Union[Any, Tuple[float, float], int] | ||
(e1, e2) = e # E: 'builtins.int' object is not iterable |
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.
Here are additional tests that would be nice to have:
- Lvalue items already have declared types, and the union rvalue has either a compatible or non-compatible type.
- In an lvalue like
x, y
onlyx
has declared type, and the type ofy
will be inferred. - Lvalue item is of form
x[i]
orx.y
with a union rvalue. - Lvalue is like
x, *y
.
if inferred: | ||
self.set_inferred_type(inferred, lv, union) | ||
else: | ||
self.store_type(lv, union) |
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.
Retrofitting like this may not be a problem, but I suspect it might be. Accumulating through joins might not be any better. The most correct approach would be to infer the entire type for an lvalue before setting it, so that intermediate values can't leak. I don't know, but it may be possible that the intermediate lvalue types leak to the rvalue in some weird corner cases.
I'd recommend not worrying about this too much, but I'd suggest adding a TODO comment about the potential issues.
_1, _2, inferred = self.check_lvalue(lv) | ||
union = join_type_list(ut) | ||
if inferred: | ||
self.set_inferred_type(inferred, lv, union) |
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.
You'll probably need to put the new type to the binder (maybe self.binder.assign_type(...)
). Also add a test case for this. Example where this may make a difference:
class A: pass
class B(A): pass
class C(A): pass
x = None # type: object
a = [] # type: Union[List[B], List[C]]
x, y = a
# now type of x should be A
x = 1 # should be ok
# now type of x should be 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.
The assignment to a
is not accepted by mypy. I opened #2164
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.
Simple assign_type
did not work; I needed something more intrusive than that. I don't like this solution, to be honest. I want to ask the type to split itself, without side effects, but that's not how things works.
When mypy is run with `--show-column-numbers`, error messages include columns. Column numbers are 0-based. Fixes python#1216.
It's just getting uglier :| Using silenced period for the binder.
…2200) * Fix strict none errors in the test subpackage. This shows how to deal with python#2199 for `p[i].arg`. * Run mypy over itself in --strict-optional mode. Add mypy.ini to suppress most errors (for now).
Also set options.show_traceback in all test modules I could find. These flags used to be implemented as globals in errors.py that had to be set by calling set_show_tb() or set_drop_into_pdb() (and setting the corresponding Options attributes had no effect). Now they're just regular options. I had to adjust some plumbing to give report_internal_error() access to the options but it was easier than I thought.
Fixes python#2212. All that's left is some calls to in_checked_function(), which replaces the old typing_mode_full() and typing_mode_none() (its negation after the elimination of typing_mode_weak()).
It's just getting uglier :| Using silenced period for the binder.
(Again sorry about the noise) |
So I used rebase instead of merge, which made the diff impossible... How can I fix it? Close and reopen the PR? |
So many users, so many git workflows... Just close and create a new PR.
|
Reopened as #2219 |
Fix #1855: the union case was simply not handled.
I moved some code around so the cases are syntactically together.
I use join instead of union, but it's always finite and most of the time very short, so using unions seems feasible.
(Probably fixes #1575 too)