Add runtime `__slots__` attribute to `attrs` (#15651)
This is similar to https://github.com/python/mypy/pull/15649 but for
`attrs` :)
Refs https://github.com/python/mypy/issues/15647
diff --git a/mypy/plugins/attrs.py b/mypy/plugins/attrs.py
index 7f1196c..0f748cc 100644
--- a/mypy/plugins/attrs.py
+++ b/mypy/plugins/attrs.py
@@ -896,6 +896,13 @@
# Unlike `@dataclasses.dataclass`, `__slots__` is rewritten here.
ctx.cls.info.slots = {attr.name for attr in attributes}
+ # Also, inject `__slots__` attribute to class namespace:
+ slots_type = TupleType(
+ [ctx.api.named_type("builtins.str") for _ in attributes],
+ fallback=ctx.api.named_type("builtins.tuple"),
+ )
+ add_attribute_to_class(api=ctx.api, cls=ctx.cls, name="__slots__", typ=slots_type)
+
def _add_match_args(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None:
if (
diff --git a/test-data/unit/check-plugin-attrs.test b/test-data/unit/check-plugin-attrs.test
index 3d1e2d7..88a541c 100644
--- a/test-data/unit/check-plugin-attrs.test
+++ b/test-data/unit/check-plugin-attrs.test
@@ -1676,6 +1676,33 @@
self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.C"
[builtins fixtures/plugin_attrs.pyi]
+[case testRuntimeSlotsAttr]
+from attr import dataclass
+
+@dataclass(slots=True)
+class Some:
+ x: int
+ y: str
+ z: bool
+
+reveal_type(Some.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str, builtins.str]"
+
+@dataclass(slots=True)
+class Other:
+ x: int
+ y: str
+
+reveal_type(Other.__slots__) # N: Revealed type is "Tuple[builtins.str, builtins.str]"
+
+
+@dataclass
+class NoSlots:
+ x: int
+ y: str
+
+NoSlots.__slots__ # E: "Type[NoSlots]" has no attribute "__slots__"
+[builtins fixtures/plugin_attrs.pyi]
+
[case testAttrsWithMatchArgs]
# flags: --python-version 3.10
import attr