Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion mypy/plugins/attrs.py
Original file line number Diff line number Diff line change
Expand Up @@ -960,7 +960,9 @@ def _add_slots(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) ->
[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)
add_attribute_to_class(
api=ctx.api, cls=ctx.cls, name="__slots__", typ=slots_type, is_classvar=True
)


def _add_match_args(ctx: mypy.plugin.ClassDefContext, attributes: list[Attribute]) -> None:
Expand Down
1 change: 1 addition & 0 deletions mypy/plugins/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -475,6 +475,7 @@ def add_slots(self, info: TypeInfo, attributes: list[DataclassAttribute]) -> Non
self._cls,
"__slots__",
slots_type,
is_classvar=True,
overwrite_existing=slots_defined_by_plugin,
)

Expand Down
1 change: 1 addition & 0 deletions mypy/semanal.py
Original file line number Diff line number Diff line change
Expand Up @@ -4648,6 +4648,7 @@ def make_name_lvalue_var(
assert self.type is not None
v.info = self.type
v.is_initialized_in_class = True
v.is_classvar = name == "__slots__"
v.allow_incompatible_override = name in ALLOW_INCOMPATIBLE_OVERRIDE
if kind != LDEF:
v._fullname = self.qualified_name(name)
Expand Down
3 changes: 3 additions & 0 deletions mypyc/irbuild/classdef.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,9 @@ def add_attr(self, lvalue: NameExpr, stmt: AssignmentStmt) -> None:
# Variable declaration with no body
if isinstance(stmt.rvalue, TempNode):
return
# Native classes use mypyc's own layout, so don't emit __slots__ at runtime.
if lvalue.name == "__slots__":
return
# Only treat marked class variables as class variables.
if not (is_class_var(lvalue) or stmt.is_final_def):
return
Expand Down
17 changes: 17 additions & 0 deletions mypyc/test-data/run-classes.test
Original file line number Diff line number Diff line change
Expand Up @@ -1175,6 +1175,23 @@ assert f() == 10
A.x = 200
assert f() == 200

[case testSlotsNotEmittedAsClassVar]
class Base:
__slots__ = ("value",)
value: int

class Child(Base):
pass

def make_child() -> Child:
child = Child()
child.value = 1
return child
[file driver.py]
from native import make_child

assert make_child().value == 1

[case testClassVarDoesNotShadowMethodGlobal]
from typing import ClassVar
from testutil import assertRaises
Expand Down
12 changes: 12 additions & 0 deletions test-data/unit/check-dataclasses.test
Original file line number Diff line number Diff line change
Expand Up @@ -1514,6 +1514,18 @@ class Some:
self.y = 1 # E: Trying to assign name "y" that is not in "__slots__" of type "__main__.Some"
[builtins fixtures/dataclasses.pyi]

[case testDataclassGeneratedSlotsClassVariableOverride]
from dataclasses import dataclass
from typing import ClassVar

@dataclass(slots=True)
class Base:
value: int

class Child(Base):
__slots__: ClassVar[tuple[str, ...]] = ()
[builtins fixtures/tuple.pyi]

[case testDataclassWithSlotsDef]
from dataclasses import dataclass

Expand Down
13 changes: 13 additions & 0 deletions test-data/unit/check-plugin-attrs.test
Original file line number Diff line number Diff line change
Expand Up @@ -1745,6 +1745,19 @@ class C:
self.c = 2 # E: Trying to assign name "c" that is not in "__slots__" of type "__main__.C"
[builtins fixtures/plugin_attrs.pyi]

[case testAttrsGeneratedSlotsClassVariableOverride]
from typing import ClassVar

import attrs

@attrs.define(slots=True)
class Base:
value: int

class Child(Base):
__slots__: ClassVar[tuple[str, ...]] = ()
[builtins fixtures/plugin_attrs.pyi]

[case testAttrsClassWithSlotsDerivedFromNonSlots]
import attrs

Expand Down
11 changes: 11 additions & 0 deletions test-data/unit/check-slots.test
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ A.b = 1
[builtins fixtures/tuple.pyi]


[case testSlotsClassVariableOverride]
from typing import ClassVar

class Base:
__slots__ = ()

class Child(Base):
__slots__: ClassVar[tuple[str, ...]] = ()
[builtins fixtures/tuple.pyi]


[case testSlotsDefinitionMultipleVars1]
class A:
__slots__ = __fields__ = ("a", "b")
Expand Down
Loading