-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Turn literal
and literal_hash
into functions
#3071
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
Conversation
The idea has been discussed briefly in #2238. |
If it means anything, the tests take the same time to execute. |
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'm honestly not sure this is worth doing. Leaving the decision up to Jukka (who may also have PRs in flight that would require rebasing against this).
mypy/checkexpr.py
Outdated
@@ -21,7 +21,7 @@ | |||
DictionaryComprehension, ComplexExpr, EllipsisExpr, StarExpr, AwaitExpr, YieldExpr, | |||
YieldFromExpr, TypedDictExpr, PromoteExpr, NewTypeExpr, NamedTupleExpr, TypeVarExpr, | |||
TypeAliasExpr, BackquoteExpr, ARG_POS, ARG_NAMED, ARG_STAR, ARG_STAR2, MODULE_REF, | |||
UNBOUND_TVAR, BOUND_TVAR, LITERAL_TYPE | |||
UNBOUND_TVAR, BOUND_TVAR, LITERAL_TYPE, literal |
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.
Trailing comma.
mypy/binder.py
Outdated
@@ -2,7 +2,7 @@ | |||
from contextlib import contextmanager | |||
|
|||
from mypy.types import Type, AnyType, PartialType | |||
from mypy.nodes import (Key, Node, Expression, Var, RefExpr, SymbolTableNode) | |||
from mypy.nodes import (Key, Expression, Var, RefExpr, literal, literal_hash) |
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.
While you're at it drop the parentheses.
mypy/checker.py
Outdated
@@ -21,7 +21,7 @@ | |||
Context, Decorator, PrintStmt, LITERAL_TYPE, BreakStmt, PassStmt, ContinueStmt, | |||
ComparisonExpr, StarExpr, EllipsisExpr, RefExpr, ImportFrom, ImportAll, ImportBase, | |||
ARG_POS, CONTRAVARIANT, COVARIANT, ExecStmt, GlobalDecl, Import, NonlocalDecl, | |||
MDEF, Node | |||
MDEF, Node, literal, literal_hash |
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.
Add trailing comma.
mypy/nodes.py
Outdated
# of an index expression, or the operands of an operator expression). | ||
|
||
|
||
def literal_hash(e: Expression) -> Key: |
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 just begging to become a visitor.
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 will use a visitor if the PR will be accepted.
(I don't know why exactly, but it feels cleaner this way. OTOH a visitor might help catching non-exhaustive match)
mypy/nodes.py
Outdated
return None | ||
|
||
|
||
def literal(e: Expression) -> 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.
(This less so.)
mypy/semanal.py
Outdated
@@ -66,7 +66,7 @@ | |||
YieldExpr, ExecStmt, Argument, BackquoteExpr, ImportBase, AwaitExpr, | |||
IntExpr, FloatExpr, UnicodeExpr, EllipsisExpr, TempNode, | |||
COVARIANT, CONTRAVARIANT, INVARIANT, UNBOUND_IMPORTED, LITERAL_YES, ARG_OPT, nongen_builtins, | |||
collections_type_aliases, get_member_expr_fullname, | |||
collections_type_aliases, get_member_expr_fullname, literal |
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.
Trailing comma.
Can you fix the merge conflicts? |
(Fixed online, I hope it'll pass) |
mypy/nodes.py
Outdated
@@ -155,9 +155,6 @@ def accept(self, visitor: StatementVisitor[T]) -> T: | |||
|
|||
class Expression(Node): | |||
"""An expression node.""" | |||
literal = LITERAL_NO | |||
literal_hash = None # type: Key |
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.
@ilevkivskyi isn't this a violation of strict optional? This value is actually read, it's not just a declaration. I assume this should be Optional[Key]
?
(This is why the build for this PR breaks; the new literal_hash()
returns None when there's no match, which is what the client code expects).
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 an exception for historical reasons. Before PEP 526 there was no way do define an instance variable without a default value x: int
. Therefore it is allowed to write this even with --strict-optional
(the same code should fail at module and function scopes).
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.
And yes, if this is not a declaration, then you should use explicitly Optional[Key]
here.
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.
OK. I will open a PR.
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 general idea of this factoring looks good to me -- it moves spread-out special-purpose logic away from node classes to a single location. This makes it easier to verify that the literal hash logic is consistent. I prefer node classes to be (at least mostly) pure data. This should also reduce memory use somewhat. Some things are needed:
I will do a detailed review once the above things have been resolved. |
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.
Explicitly requesting the above changes so that the review status is shown correctly in the PR list.
Conflicts: mypy/binder.py mypy/nodes.py
I will make the visitor later, it's much more verbose (code duplication and implementing empty methods) so I'll try to see the cleanest way to do it. In the meantime, is it possible to check the performance on against the internal codebase before that? I am not sure that a visitor will be faster, since it adds a method call (or two) at each level, and several cases are grouped to a single isinstance check. |
|
There are no checks against ABCs in this code. |
@JukkaL did you have the opportunity to test the performance implications of this PR? |
I'm now back from vacation but I'll have more vacation starting a week from now. I may be able to check performance next week, but not sure yet. |
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 for the updates! Looks good now (just a few minor comments). I verified the performance with internal Dropbox repos and mypy actually seems slightly faster with this PR than before, which is excellent. Memory should be lower as well, but I didn't check it. The suggested changes are optional except for the IndexExpr
thing.
mypy/nodes.py
Outdated
|
||
elif isinstance(e, IndexExpr): | ||
if literal(e.index) == LITERAL_YES: | ||
return literal(e.base) |
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.
Add else where we return LITERAL_NO
. Otherwise, it looks like we can return LITERAL_YES
because of the if literal_hash(e):
check.
mypy/nodes.py
Outdated
return ('Index', literal_hash(e.base), literal_hash(e.index)) | ||
return None | ||
|
||
def visit_call_expr(self, e: CallExpr) -> 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 you can have a single method definition and then create a bunch of aliases. Example (not tried):
def visit_non_literal(self, e: Node) -> None:
return None
visit_call_expr = visit_non_literal
visit_slice_expr = visit_non_literal
...
This would reduce some of the verbosity.
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 result in Incompatible types in assignment (expression has type Callable[[Node], None], base class "ExpressionVisitor" defined the type as Callable[[CallExpr], Tuple[Any, ...]])
Which kinda looks like a bug to me since the returned value should be defined as Optional[Key]
.
mypy/nodes.py
Outdated
@@ -2559,3 +2470,215 @@ def check_arg_names(names: List[Optional[str]], nodes: List[T], fail: Callable[[ | |||
fail("Duplicate argument '{}' in {}".format(name, description), node) | |||
break | |||
seen_names.add(name) | |||
|
|||
# [Note Literals and literal_hash] |
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.
Might be worth moving all the literal and literal hash specific code and comments to a new module to make mypy.nodes
smaller. After all, these aren't relevant for most users of nodes. For example, it could be named mypy.literals
.
Thanks for the updates! This almost ready to be merged. Can you fix the merge conflicts and the build failures? |
Oops. Sorry about that, I got busy and didn't look at the build results. Just a moment. |
Done |
This PR groups the handling of literal/literal_hash in one place, and removes the auxiliary attributes. I think it is clearer this way, and removes clutter from nodes.py. It is easier to check for consistency (e.g. serialization is not possible at all).
I don't know what is the impact performance-wise; in general it should reduce memory footprint for AST nodes (many can become NamedTuple now), which might have some cost in execution time. If it is noticeable, I guess
lru_cache
can eliminate that.The functions can be moved to a different module (binder.py?) but they curry dependencies with them.