Skip to content

Commit

Permalink
Fix crash on overriding with frozen attrs (#14186)
Browse files Browse the repository at this point in the history
Fixes #6715 

Fix is straightforward, currently we assume that if we have a variable
in MRO, and its name appears in current class, it is from this class,
which in fact may not be the case when a variable is overridden with a
property or method.

I also add a test case for a crash that was previously reported in the
same issue but is already (accidentally?) fixed.
  • Loading branch information
ilevkivskyi authored Nov 25, 2022
1 parent a9024a8 commit 278a095
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 1 deletion.
6 changes: 5 additions & 1 deletion mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,11 @@ def _make_frozen(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute])
if attribute.name in ctx.cls.info.names:
# This variable belongs to this class so we can modify it.
node = ctx.cls.info.names[attribute.name].node
assert isinstance(node, Var)
if not isinstance(node, Var):
# The superclass attribute was overridden with a non-variable.
# No need to do anything here, override will be verified during
# type checking.
continue
node.is_property = True
else:
# This variable belongs to a super class so create new Var so we
Expand Down
46 changes: 46 additions & 0 deletions test-data/unit/check-attr.test
Original file line number Diff line number Diff line change
Expand Up @@ -1788,3 +1788,49 @@ class C:
c = C(x=[C.D()])
reveal_type(c.x) # N: Revealed type is "builtins.list[__main__.C.D]"
[builtins fixtures/list.pyi]

[case testRedefinitionInFrozenClassNoCrash]
import attr

@attr.s
class MyData:
is_foo: bool = attr.ib()

@staticmethod # E: Name "is_foo" already defined on line 5
def is_foo(string: str) -> bool: ...
[builtins fixtures/classmethod.pyi]

[case testOverrideWithPropertyInFrozenClassNoCrash]
from attrs import frozen

@frozen(kw_only=True)
class Base:
name: str

@frozen(kw_only=True)
class Sub(Base):
first_name: str
last_name: str

@property
def name(self) -> str: ...
[builtins fixtures/property.pyi]

[case testOverrideWithPropertyInFrozenClassChecked]
from attrs import frozen

@frozen(kw_only=True)
class Base:
name: str

@frozen(kw_only=True)
class Sub(Base):
first_name: str
last_name: str

@property
def name(self) -> int: ... # E: Signature of "name" incompatible with supertype "Base"

# This matches runtime semantics
reveal_type(Sub) # N: Revealed type is "def (*, name: builtins.str, first_name: builtins.str, last_name: builtins.str) -> __main__.Sub"
[builtins fixtures/property.pyi]

0 comments on commit 278a095

Please sign in to comment.