Do not blindly undefer on leaving fuction by ilevkivskyi · Pull Request #18674 · python/mypy (original) (raw)
OK, so here is the analysis for mypy_primer:
homeassistant: An existing bug, really simple one so going to push a fix to this PR.
porcupine: a "legitimate" error, because we only allow 2 passes in type checker. IMO we should allow at least 3 (for comparison semantic analyzer does 20 passes). @JukkaL what do you think?
spark and spack: existing bug, type narrowing is not propagated to nested function if it is deferred. Test case:
[case testNarrowInFunctionDefer]
from typing import Optional, Callable, TypeVar
def top() -> None:
x: Optional[int]
assert x is not None
def foo() -> None:
defer()
reveal_type(x) # N: Revealed type is "builtins.int"
T = TypeVar("T")
def deco(fn: Callable[[], T]) -> Callable[[], T]: ...
@deco
def defer() -> int: ...
cc @JukkaL who initially implemented this feature. IIUC possible fix is to save/restore full binder stack when checking deferred functions. Alternatively we may consider only deferring top-level functions, instead of the nested ones. IMO performance impact from this will be small.
discord.py: An existing bug, and a problematic one. Right now we have a rule that if a subclass assigns to an attribute on self then we only create a new Var if either there is an explicit annotation, or no superclasses define this attribute. However methods during semantic analysis are processed in a random order, so we don't know if the attribute was defined in superclass. Test case:
[case testForwardBaseDeferAttr]
from typing import Optional, Callable, TypeVar
class C(B):
def a(self) -> None:
reveal_type(self._foo) # Revealed type is "Union[builtins.int, None]"
self._foo = defer()
class B:
def __init__(self) -> None:
self._foo: Optional[int] = None
T = TypeVar("T")
def deco(fn: Callable[[], T]) -> Callable[[], T]: ...
@deco
def defer() -> int: ...
I remember fixing something similar for the daemon was a huge pain (and I am not even sure we fixed it completely, see e.g. saved_class_attrs). It looks like we will need to do something for non-daemon mode as well. One possible way would be order methods by their .info MRO inclusion.