-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
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
Defer subclass methods if superclass has not been analyzed #5637
Conversation
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 working on this issue -- when users hit this issue, it's very confusing. It's totally unclear why mypy is complaining and how to fix the problem.
Left detailed comments since deferred nodes are a very tricky part of mypy. Once you are updating this code it's a good opportunity to add some more documentation.
mypy/checker.py
Outdated
|
||
def check_method_override_for_base_with_name( | ||
self, defn: Union[FuncBase, Decorator], name: str, base: TypeInfo) -> None: | ||
self, defn: Union[FuncBase, Decorator], name: str, base: TypeInfo) -> 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.
Document the return type.
It might be better to use concrete types in the union type, at least if it would let us remove the assert below.
mypy/semanal.py
Outdated
@@ -317,7 +317,7 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, | |||
del self.cur_mod_node | |||
del self.globals | |||
|
|||
def refresh_partial(self, node: Union[MypyFile, FuncItem, OverloadedFuncDef], | |||
def refresh_partial(self, node: Union[MypyFile, FuncItem, OverloadedFuncDef, Decorator], |
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.
We should never get a Decorator
here, since this is only used in fine-grained mode? We should either remove Decorator
from the union (preferable) or assert that node
is not a Decorator
. I think that it would be better to have the assert as early as possible, i.e. in mypy.server.update
.
mypy/semanal_pass3.py
Outdated
@@ -73,7 +73,7 @@ def visit_file(self, file_node: MypyFile, fnam: str, options: Options, | |||
del self.cur_mod_node | |||
self.patches = [] | |||
|
|||
def refresh_partial(self, node: Union[MypyFile, FuncItem, OverloadedFuncDef], | |||
def refresh_partial(self, node: Union[MypyFile, FuncItem, OverloadedFuncDef, Decorator], |
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.
Similar to the comment related to mypy.semanal.py
.
mypy/server/aststrip.py
Outdated
@@ -52,7 +52,7 @@ | |||
from mypy.typestate import TypeState | |||
|
|||
|
|||
def strip_target(node: Union[MypyFile, FuncItem, OverloadedFuncDef]) -> None: | |||
def strip_target(node: Union[MypyFile, FuncItem, OverloadedFuncDef, Decorator]) -> 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.
Similar to the comment related to mypy.senanal.py
.
mypy/server/update.py
Outdated
@@ -1058,7 +1058,7 @@ def is_verbose(manager: BuildManager) -> bool: | |||
|
|||
|
|||
def target_from_node(module: str, | |||
node: Union[FuncDef, MypyFile, OverloadedFuncDef, LambdaExpr] | |||
node: Union[FuncDef, MypyFile, OverloadedFuncDef, LambdaExpr, Decorator] |
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.
Again, could we prevent this function from being called with a Decorator
argument earlier on?
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 can remove Decorator
from all these unions, but this will require a couple of asserts.
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 it's better to have the caller ensure that the function is called with the correct types. The annotation as it is now is arguably misleading.
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 is arguably not more misleading than it was before, it already accepts LambdaExpr
, but then fails with assert False
on it (as well as the other functions above, they accept LambdaExpr
while they shouldn't). Anyway, I will try to clean this up.
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.
Ah, good point! In that case maybe LambdaExpr
should also be removed from the annotation?
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.
Yes, I will remove both.
mypy/checker.py
Outdated
@@ -72,14 +72,15 @@ | |||
|
|||
DEFAULT_LAST_PASS = 1 # type: Final # Pass numbers start at 0 | |||
|
|||
DeferredNodeType = Union[FuncDef, LambdaExpr, MypyFile, OverloadedFuncDef, Decorator] | |||
|
|||
# A node which is postponed to be processed during the next pass. | |||
# This is used for both batch mode and fine-grained incremental mode. | |||
DeferredNode = NamedTuple( | |||
'DeferredNode', | |||
[ | |||
# In batch mode only FuncDef and LambdaExpr are supported |
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 comment seems out of date.
mypy/checker.py
Outdated
@@ -72,14 +72,15 @@ | |||
|
|||
DEFAULT_LAST_PASS = 1 # type: Final # Pass numbers start at 0 | |||
|
|||
DeferredNodeType = Union[FuncDef, LambdaExpr, MypyFile, OverloadedFuncDef, Decorator] |
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 comment about which node types can be deferred in fine-grained vs. normal modes.
Also mention that nested functions can't be deferred -- only top-level functions and methods of classes not defined within a function can be deferred.
mypy/checker.py
Outdated
type_name = self.errors.type_name[-1] | ||
else: | ||
type_name = None | ||
# Shouldn't we freeze the entire 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.
This comment is kind of vague. Maybe this should be a TODO comment?
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 am not sure what was the intent. This comment is two years old, maybe just remove it?
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 reword it like this: "We don't freeze the entire scope since only top-level functions and methods can be deferred. Only module/class level scope information is needed. Module-level scope information is preserved in the TypeChecker instance." (I think that this is correct.)
mypy/checker.py
Outdated
@@ -1257,11 +1258,16 @@ def check_method_override(self, defn: Union[FuncBase, Decorator]) -> None: | |||
"""Check if function definition is compatible with base classes.""" |
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.
Update docstring to mention that this may defer the method if a signature is not available in a base class.
@JukkaL Thanks for review! I addressed the comments. I decided to have a separate I also added a bunch of tests combining overloads, decorators, variables, double deferrals, and super calls (also with various combinations of these). |
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 cleanups! Looks better. I left a few more comments. Feel free to merge once you've addressed them.
mypy/checker.py
Outdated
def check_method_override(self, defn: Union[FuncDef, OverloadedFuncDef, Decorator]) -> None: | ||
"""Check if function definition is compatible with base classes. | ||
|
||
This may defer the method if a signature is not available in at least on base class. |
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.
Typo: "on" -> "one"
def deco(f: Callable[..., T]) -> Callable[..., Tuple[T, int]]: ... | ||
[out] | ||
|
||
[case testCyclicDecoratorSuperDeferred] |
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.
Did you verify that these test cases trigger deferral? Using from ... import
can be used to make it more explicit in which order modules are processed by mypy.
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 actually checked only first two tests I added, I will also double-check rest just in 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.
They all get deferred expected number of times (1, 2, or 3), I switched to from ... import
in half of the tests anyway.
mypy/checker.py
Outdated
@@ -72,19 +73,31 @@ | |||
|
|||
DEFAULT_LAST_PASS = 1 # type: Final # Pass numbers start at 0 | |||
|
|||
DeferredNodeType = Union[FuncDef, LambdaExpr, OverloadedFuncDef, Decorator] | |||
FineDeferredNodeType = Union[FuncDef, MypyFile, OverloadedFuncDef] |
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: I'd prefer to call this FineGrainedDeferredNodeType
or something.
We don't use "fine" as a shorted version of "fine-grained" elsewhere and it can be confusing.
mypy/checker.py
Outdated
('context_type_name', Optional[str]), # Name of the surrounding class (for error messages) | ||
('active_typeinfo', Optional[TypeInfo]), # And its TypeInfo (for semantic analysis | ||
# self type handling) | ||
]) | ||
|
||
# Same as above, but for fine-grained mode targets. Only top-level functions/methods | ||
# and module top levels are allowed as such. | ||
FineDeferredNode = NamedTuple( |
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.
Similar to above, I'd prefer to name this FineGrainedDeferredNode
.
Fixes #5560
Fixes #5548
Half of the PR is updating various function signatures to also accept
Decorator
. I still prohibitDecorator
as a target in fine-grained mode, but I think there should be no harm to defer it in normal mode.