Skip to content

Commit 278a095

Browse files
authored
Fix crash on overriding with frozen attrs (#14186)
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.
1 parent a9024a8 commit 278a095

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

mypy/plugins/attrs.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,11 @@ def _make_frozen(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute])
736736
if attribute.name in ctx.cls.info.names:
737737
# This variable belongs to this class so we can modify it.
738738
node = ctx.cls.info.names[attribute.name].node
739-
assert isinstance(node, Var)
739+
if not isinstance(node, Var):
740+
# The superclass attribute was overridden with a non-variable.
741+
# No need to do anything here, override will be verified during
742+
# type checking.
743+
continue
740744
node.is_property = True
741745
else:
742746
# This variable belongs to a super class so create new Var so we

test-data/unit/check-attr.test

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,3 +1788,49 @@ class C:
17881788
c = C(x=[C.D()])
17891789
reveal_type(c.x) # N: Revealed type is "builtins.list[__main__.C.D]"
17901790
[builtins fixtures/list.pyi]
1791+
1792+
[case testRedefinitionInFrozenClassNoCrash]
1793+
import attr
1794+
1795+
@attr.s
1796+
class MyData:
1797+
is_foo: bool = attr.ib()
1798+
1799+
@staticmethod # E: Name "is_foo" already defined on line 5
1800+
def is_foo(string: str) -> bool: ...
1801+
[builtins fixtures/classmethod.pyi]
1802+
1803+
[case testOverrideWithPropertyInFrozenClassNoCrash]
1804+
from attrs import frozen
1805+
1806+
@frozen(kw_only=True)
1807+
class Base:
1808+
name: str
1809+
1810+
@frozen(kw_only=True)
1811+
class Sub(Base):
1812+
first_name: str
1813+
last_name: str
1814+
1815+
@property
1816+
def name(self) -> str: ...
1817+
[builtins fixtures/property.pyi]
1818+
1819+
[case testOverrideWithPropertyInFrozenClassChecked]
1820+
from attrs import frozen
1821+
1822+
@frozen(kw_only=True)
1823+
class Base:
1824+
name: str
1825+
1826+
@frozen(kw_only=True)
1827+
class Sub(Base):
1828+
first_name: str
1829+
last_name: str
1830+
1831+
@property
1832+
def name(self) -> int: ... # E: Signature of "name" incompatible with supertype "Base"
1833+
1834+
# This matches runtime semantics
1835+
reveal_type(Sub) # N: Revealed type is "def (*, name: builtins.str, first_name: builtins.str, last_name: builtins.str) -> __main__.Sub"
1836+
[builtins fixtures/property.pyi]

0 commit comments

Comments
 (0)