Skip to content
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

Closed
wants to merge 54 commits into from

Conversation

elazarg
Copy link
Contributor

@elazarg elazarg commented Sep 18, 2016

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)

@elazarg elazarg changed the title Multiassign from Union Fix #1855: Multiassign from Union Sep 18, 2016
@elazarg elazarg changed the title Fix #1855: Multiassign from Union Fix #1855: Multiassign from Union (in progress) Sep 19, 2016
@elazarg elazarg changed the title Fix #1855: Multiassign from Union (in progress) Fix #1855: Multiassign from Union Sep 19, 2016
if inferred:
self.set_inferred_type(inferred, lv, union)
else:
self.store_type(lv, union)
Copy link
Contributor Author

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?

Copy link
Collaborator

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.

@gvanrossum
Copy link
Member

I think this is great but I don't know that code very well so I hope @JukkaL can review. Have you looked at #2128 yet?

@elazarg
Copy link
Contributor Author

elazarg commented Sep 19, 2016

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.

@gvanrossum
Copy link
Member

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 __radd__ methods might help (or might make things worse, see. e.g. #2129 and the issues referenced from there).

@elazarg
Copy link
Contributor Author

elazarg commented Sep 19, 2016

i = None  # type: int
f = None  # type: float
i + f

I get error: Unsupported operand types for + ("int" and "float")

@JukkaL
Copy link
Collaborator

JukkaL commented Sep 19, 2016

@elazarg You'll likely need overloads in your builtins fixture for that to work in a test case.

@elazarg
Copy link
Contributor Author

elazarg commented Sep 19, 2016

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:
Copy link
Collaborator

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:
Copy link
Collaborator

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

Copy link
Contributor Author

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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

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
Copy link
Collaborator

@JukkaL JukkaL Sep 21, 2016

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 only x has declared type, and the type of y will be inferred.
  • Lvalue item is of form x[i] or x.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)
Copy link
Collaborator

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)
Copy link
Collaborator

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

Copy link
Contributor Author

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

Copy link
Contributor Author

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.

elazarg and others added 11 commits October 1, 2016 19:25
…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()).
@gvanrossum gvanrossum changed the title Fix #1855: Multiassign from Union [conflict] Fix #1855: Multiassign from Union Oct 4, 2016
@elazarg
Copy link
Contributor Author

elazarg commented Oct 5, 2016

(Again sorry about the noise)

@elazarg
Copy link
Contributor Author

elazarg commented Oct 5, 2016

So I used rebase instead of merge, which made the diff impossible... How can I fix it? Close and reopen the PR?

@gvanrossum
Copy link
Member

gvanrossum commented Oct 5, 2016 via email

@elazarg
Copy link
Contributor Author

elazarg commented Oct 5, 2016

Reopened as #2219

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

error: 'Union[Any, Tuple[Any, Any]]' object is not iterable