From 81d082cb34829ce94007cc337b24a924f599409b Mon Sep 17 00:00:00 2001 From: Jake Lishman Date: Fri, 8 Jul 2022 15:07:15 +0100 Subject: [PATCH] Fix crash overriding partial-type attribute with method (#12943) Attributes can still be partially typed (e.g. ``) after a parent-class definition if the block they are declared in is deferred. The first pass for child classes might then encounter this type when considering method overrides, which could cause a crash when attempting to determine subtype compatibility. Fixes #11686 Fixes #11981 --- mypy/checker.py | 4 +++- test-data/unit/check-classes.test | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/mypy/checker.py b/mypy/checker.py index 60be940a327cf..d17871039332b 100644 --- a/mypy/checker.py +++ b/mypy/checker.py @@ -1579,7 +1579,9 @@ def check_method_override_for_base_with_name( # it can be checked for compatibility. original_type = get_proper_type(base_attr.type) original_node = base_attr.node - if original_type is None: + # `original_type` can be partial if (e.g.) it is originally an + # instance variable from an `__init__` block that becomes deferred. + if original_type is None or isinstance(original_type, PartialType): if self.pass_num < self.last_pass: # If there are passes left, defer this node until next pass, # otherwise try reconstructing the method type from available information. diff --git a/test-data/unit/check-classes.test b/test-data/unit/check-classes.test index ee560de892084..e326e24df0e6d 100644 --- a/test-data/unit/check-classes.test +++ b/test-data/unit/check-classes.test @@ -151,6 +151,24 @@ class Derived(Base): __hash__ = 1 # E: Incompatible types in assignment (expression has type "int", base class "Base" defined the type as "Callable[[Base], int]") +[case testOverridePartialAttributeWithMethod] +# This was crashing: https://github.com/python/mypy/issues/11686. +class Base: + def __init__(self, arg: int): + self.partial_type = [] # E: Need type annotation for "partial_type" (hint: "partial_type: List[] = ...") + self.force_deferral = [] + + # Force inference of the `force_deferral` attribute in `__init__` to be + # deferred to a later pass by providing a definition in another context, + # which means `partial_type` remains only partially inferred. + force_deferral = [] # E: Need type annotation for "force_deferral" (hint: "force_deferral: List[] = ...") + + +class Derived(Base): + def partial_type(self) -> int: # E: Signature of "partial_type" incompatible with supertype "Base" + ... + + -- Attributes -- ----------