8000 Explicit types for bindables ("Tighten Types" continued) by elazarg · Pull Request #2238 · python/mypy · GitHub
[go: up one dir, main page]

Skip to content

Explicit types for bindables ("Tighten Types" continued) #2238

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

Merged
merged 19 commits into from
Mar 27, 2017

Conversation

elazarg
Copy link
Contributor
@elazarg elazarg commented Oct 11, 2016

Currently the only IndexExpr, MemberExpr and NameExpr are bound through ConditionalTypeBinder. This information is made explicit, although it's still kept inside binder, since I think it's not unlikely to change. (I was wrong thinking this is about lvalues. It is not really related to syntax classes).

The key of Frame is an object instead of Any - I'd vote for str or int - and so is literal_hash, which is moved from Node to Expression. (I think literal_hash and literal should be standalone functions instead of fields, since it's an implementation detail of the binder and possibly other mappings, and most of the time it does not interest the reader of the syntax classes).


While at it, push() is changed to put_if_bindable(). IMHO it is much more precise about what's actually promised. Similarly assign_type() becomes assign_type_if_bindable().

@elazarg elazarg mentioned this pull request Oct 13, 2016
@gvanrossum
Copy link
Member

Let's make sure to add "Fixes #2222" to the commit message when squash-merging this.

@rwbarton
Copy link
Contributor

I have a large patch to the binder in progress (#1748), so can we hold off on this for now? I don't want to have to rebase yet again.

I'll be happy to take a look at this once #1748 lands.

@JukkaL
Copy link
Collaborator
JukkaL commented Oct 14, 2016

I hope I can review #1748 on Monday or over the weekend. It's a big change and has been open for a long time so let's land it first.

@gvanrossum gvanrossum changed the title Explicit types for bindables ("Tighten Types" continued) [waiting for #1748] Explicit types for bindables ("Tighten Types" continued) Oct 17, 2016
Conflicts:
	mypy/binder.py
	mypy/checker.py
	mypy/checkexpr.py
	mypy/nodes.py
@elazarg elazarg changed the title [waiting for #1748] Explicit types for bindables ("Tighten Types" continued) Explicit types for bindables ("Tighten Types" continued) Oct 18, 2016
Copy link
Member
@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks decent except for one thing I don't quite get in a quick scan. @rwbarton what's your opinion of this PR?

if not var.type:
if var.type:
return var.type
else:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just double-checking that what you changed here isn't an accidental merge conflict? You've done more here than just revers the sense of the check and swap the then/else clauses.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part is related to #2222 - binder.get() does not accept Var; val is always None.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, I would like to extract literal-handling to a couple of functions, and avoid putting them in the Expression classes. I already have the branch ready. Will you consider it? It does not solve anything, but I think it's nice to have this information in a single place, instead of scattered around.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be worth calling out this "change" explicitly in the commit message, since it does not obviously look like a mere refactoring.

mypy/nodes.py Outdated
self.literal_hash = ((cast(Any, 'Comparison'),) + tuple(operators) +
tuple(o.literal_hash for o in operands))
self.literal_hash = (('Comparison',) + tuple(operators) +
tuple(o.literal_hash for o in operands)) # type: ignore
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Explain the type ignores here and below?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was supposed to be another way to spell the same thing, but it's not since Tuple[Any, ...] is still informative. I should revert this.

@gvanrossum
Copy link
Member
gvanrossum commented Oct 25, 2016 via email

@rwbarton
Copy link
Contributor

I still intend to get around to looking at this soon.

@gvanrossum
Copy link
Member

I'm sorry, I've lost track of this. Are you waiting for me or am I waiting for you?

@elazarg
Copy link
Contributor Author
elazarg commented Oct 29, 2016

I am waiting

mypy/binder.py Outdated
@@ -189,19 +197,20 @@ def pop_frame(self, can_skip: bool, fall_through: int) -> Frame:

return result

def get_declaration(self, expr: Node) -> Type:
def get_declaration(self, expr: BindableExpression) -> Type:
assert not isinstance(expr, SymbolTableNode)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assert can't trigger given the definition of BindableExpression right? Or am I misunderstanding?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a leftover from an old check,
if isinstance(node, (RefExpr, SymbolTableNode)) and isinstance(node.node, Var):
I should remove it.

@gvanrossum
Copy link
Member

This looks all right to me! I'll wait maybe a day for @rwbarton to speak up.


BindableTypes = (IndexExpr, MemberExpr, NameExpr)
BindableExpression = Union[IndexExpr, MemberExpr, NameExpr]

Copy link
Contributor
67E6

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the point of introducing this BindableExpression. It seems to duplicate information with which Expressions have (non-None) literals. It should be fine to just use Expression everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lists of literals have non-None literals.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here's the branch with literal() and literal_hash() functions:
https://github.com/elazarg/mypy/blob/literal_hash/mypy/nodes.py#L2351-L2421

mypy/binder.py Outdated
@@ -103,19 +109,21 @@ def _get(self, key: Key, index: int=-1) -> Type:
return self.frames[i][key]
return None

def push(self, node: Node, typ: Type) -> None:
if not node.literal:
def put_if_bindable(self, expr: Expression, typ: Type) -> None:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the change to put, but _if_bindable doesn't add any information (there's no contrasting put_even_if_not_bindable) and is pretty long.

Basically, put is just the way for the rest of the type checker to pass information into the binder, and I think of it as the binder's business whether it bothers to record a piece of information it is given or not.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it adds no information only if you know exactly how the binder works. When I read this code I had no idea that some expressions can be simply ignored. I do not like long names, but shorter names feels misleading.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course it is the binder's business, but the reader should know that it is possible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A docstring (or general documentation on the binder) seems like a better vehicle for that.

mypy/binder.py Outdated
if not node.literal:
def put_if_bindable(self, expr: Expression, typ: Type) -> None:
if not isinstance(expr, BindableTypes):
return
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not really sure about the need for this.

mypy/binder.py Outdated
type: Type,
declared_type: Type,
restrict_any: bool = False) -> None:
def assign_type_if_bindable(self, expr: Expression,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, _if_bindable seems extraneous.

mypy/binder.py Outdated
declared_type: Type,
restrict_any: bool = False) -> None:
if not isinstance(expr, BindableTypes):
return None
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check doesn't seem necessary.

if not var.type:
if var.type:
return var.type
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be worth calling out this "change" explicitly in the commit message, since it does not obviously look like a mere refactoring.

@rwbarton
Copy link
Contributor
rwbarton commented Nov 3, 2016

I kind of feel like the addition of BindableTypes and BindableExpression goes either too far or not far enough. It seems like a poor compromise at the moment to list these types when there is no actual need to do so; the information is already contained in nodes.py. Concretely, if someone wanted to add another sort of expression to be tracked by the binder, they would have to remember to change both nodes.py and this BindableTypes, or their new types would be automatically discarded by the new check in put.

If you were to move literal and literal_hash to functions in the binder as you suggest in the PR comment, then this approach could make sense.

< 10000 p class="ml-1 mb-2 mt-2" data-show-on-error hidden> Sorry, something went wrong.

@elazarg
Copy link
Contributor Author
elazarg commented Nov 3, 2016

I have moved literal* outside of nodes.py in another branch; the only culprit is that it's ugly to introduce all the imports needed inside binder.py. I would like to have a dedicated module for that. But even a function inside nodes.py is nice.

You say "this information is already contained in nodes.py" - I believe you that the information is technically available, but I still can't say where and how, precisely. (Of course it could be just me). Only during the work on this PR did I understand what does the binder do, and I wanted to make this information explicit (using "if_bindable" and "put").

I agree that the decision what is bindable is not inherent to the types themselves and could be changed transparently, but there should be such explicit concept, with a dedicated filtering function as documentation. The current policy is not just about "having a literal value", so Expression does not cut it; there are many expression types, many possible literals (e.g. List), but only 3 types are (currently) bindable. So BindableTypes is very informative as an implementation detail. I don't understand what change should be made in nodes.py in order to track another sort of expressions.
I want to keep the API about Expression, not about BindableTypes, so I should change get_declaration to avoid it. However, implementation methods such as invalidate_dependencies should use this type when possible - simply because they can.

@gvanrossum
Copy link
Member

@rwbarton and @elazarg, what is needed to get agreement on this PR to be merged?

Personally I was okay with it before, but it seems @rwbarton wants some changes. Maybe @elazarg should just make the requested changes? Personally I agree with @rwbarton that the '_if_bindable' suffix you added to some method names is unneeded -- maybe that's the last thing standing between approval?

There's also a merge conflict when I try to apply this (never mind what GitHub says).

@elazarg
Copy link
Contributor Author
elazarg commented Nov 9, 2016

@gvanrossum I reverted the _if_bindable. I do not agree, or maybe do not understand, the other comments about BindableExpression duplicating information.

@gvanrossum
Copy link
Member
gvanrossum commented Nov 9, 2016 via email

@gvanrossum
Copy link
Member

This PR is still waiting for some kind of agreement between @rwbarton and @elazarg.

@gvanrossum
Copy link
Member

This PR is still waiting for some kind of agreement between @rwbarton and @elazarg...

@elazarg
Copy link
Contributor Author
elazarg commented Jan 10, 2017

@rwbarton, will it help if I'll add the literal and literal_hash functions to this PR? I don't think it really has to do anything with it, but if you think it does (judging by the your last sentence), I will not object.

Besides, I really like these functions.

elazarg and others added 2 commits March 16, 2017 01:44
Conflicts:
	mypy/checker.py
	mypy/nodes.py
I'd like to merge this if it still works, but am a bit worried about it being stale, so I'm triggering the tests.
@gvanrossum
Copy link
Member

@elazarg Can you rebase and address the now-failing tests? We're not waiting for Reid.

@elazarg
Copy link
Contributor Author
elazarg commented Mar 27, 2017

Sure.

Copy link
Member
@gvanrossum gvanrossum left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll merge if this passes with our internal codebase.

def assign_type(self, expr: Expression,
type: Type,
declared_type: Type,
restrict_any: bool = False) -> None:
# TODO: replace with isinstance(expr, BindableTypes)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you file an issue for that regression? What does reveal_type(BindableTypes) show at this point?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Filed: #3068. It will show Expression.

@gvanrossum gvanrossum merged commit 6dc65f6 into python:master Mar 27, 2017
@gvanrossum
Copy link
Member

Thanks, I found nothing suspicious running this against our internal codebase. Hopefully @JukkaL's pending PRs aren't too much affected.

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.

4 participants
0