Skip to content

Commit b1c438d

Browse files
authored
Adds slots=True support for @attr.s, refs #11487 (#11489)
Closes #11487 Refs #11482 Refs #10801
1 parent 231f7cf commit b1c438d

File tree

2 files changed

+39
-0
lines changed

2 files changed

+39
-0
lines changed

mypy/plugins/attrs.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext',
272272
init = _get_decorator_bool_argument(ctx, 'init', True)
273273
frozen = _get_frozen(ctx, frozen_default)
274274
order = _determine_eq_order(ctx)
275+
slots = _get_decorator_bool_argument(ctx, 'slots', False)
275276

276277
auto_attribs = _get_decorator_optional_bool_argument(ctx, 'auto_attribs', auto_attribs_default)
277278
kw_only = _get_decorator_bool_argument(ctx, 'kw_only', False)
@@ -302,6 +303,8 @@ def attr_class_maker_callback(ctx: 'mypy.plugin.ClassDefContext',
302303
return
303304

304305
_add_attrs_magic_attribute(ctx, raw_attr_types=[info[attr.name].type for attr in attributes])
306+
if slots:
307+
_add_slots(ctx, attributes)
305308

306309
# Save the attributes so that subclasses can reuse them.
307310
ctx.cls.info.metadata['attrs'] = {
@@ -727,6 +730,12 @@ def _add_attrs_magic_attribute(ctx: 'mypy.plugin.ClassDefContext',
727730
)
728731

729732

733+
def _add_slots(ctx: 'mypy.plugin.ClassDefContext',
734+
attributes: List[Attribute]) -> None:
735+
# Unlike `@dataclasses.dataclass`, `__slots__` is rewritten here.
736+
ctx.cls.info.slots = {attr.name for attr in attributes}
737+
738+
730739
class MethodAdder:
731740
"""Helper to add methods to a TypeInfo.
732741

test-data/unit/check-attr.test

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1402,3 +1402,33 @@ class A:
14021402
reveal_type(A.__attrs_attrs__) # N: Revealed type is "Tuple[attr.Attribute[builtins.int], attr.Attribute[builtins.str]]"
14031403

14041404
[builtins fixtures/attr.pyi]
1405+
1406+
[case testAttrsClassWithSlots]
1407+
import attr
1408+
1409+
@attr.s(slots=True)
1410+
class A:
1411+
b: int = attr.ib()
1412+
1413+
def __attrs_post_init__(self) -> None:
1414+
self.b = 1
1415+
self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.A"
1416+
1417+
@attr.dataclass(slots=True)
1418+
class B:
1419+
__slots__ = () # would be replaced
1420+
b: int
1421+
1422+
def __attrs_post_init__(self) -> None:
1423+
self.b = 1
1424+
self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.B"
1425+
1426+
@attr.dataclass(slots=False)
1427+
class C:
1428+
__slots__ = () # would not be replaced
1429+
b: int
1430+
1431+
def __attrs_post_init__(self) -> None:
1432+
self.b = 1 # E: Trying to assign name "b" that is not in "__slots__" of type "__main__.C"
1433+
self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.C"
1434+
[builtins fixtures/attr.pyi]

0 commit comments

Comments
 (0)