From 36bd7ece64f74c85d343831dc46a02926ef7126a Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 22 Dec 2021 23:40:41 +0300 Subject: [PATCH 1/3] Fixes `__slots__` regression in 0.930, refs #11821 --- mypy/plugins/dataclasses.py | 9 ++++--- test-data/unit/check-dataclasses.test | 37 +++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 3 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index 6d78bc17e615..e3daec58922f 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -221,9 +221,12 @@ def add_slots(self, self._ctx.reason, ) return - if info.slots is not None or info.names.get('__slots__'): + + generated_slots = {attr.name for attr in attributes} + if ((info.slots is not None and info.slots != generated_slots) + or info.names.get('__slots__')): # This means we have a slots conflict. - # Class explicitly specifies `__slots__` field. + # Class explicitly specifies a different `__slots__` field. # And `@dataclass(slots=True)` is used. # In runtime this raises a type error. self._ctx.api.fail( @@ -234,7 +237,7 @@ def add_slots(self, ) return - info.slots = {attr.name for attr in attributes} + info.slots = generated_slots def reset_init_only_vars(self, info: TypeInfo, attributes: List[DataclassAttribute]) -> None: """Remove init-only vars from the class and reset init var declarations.""" diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 73476a646c99..7e6f55f7a863 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1456,3 +1456,40 @@ class Other: __slots__ = ('x',) x: int [builtins fixtures/dataclasses.pyi] + + +[case testSlotsDefinitionWithTwoPasses1] +# flags: --python-version 3.10 +# https://github.com/python/mypy/issues/11821 +from typing import TypeVar, Protocol, Generic +from dataclasses import dataclass + +C = TypeVar("C", bound="Comparable") + +class Comparable(Protocol): + pass + +V = TypeVar("V", bound=Comparable) + +@dataclass(slots=True) +class Node(Generic[V]): # Error was here + data: V +[builtins fixtures/dataclasses.pyi] + +[case testSlotsDefinitionWithTwoPasses2] +# flags: --python-version 3.10 +from typing import TypeVar, Protocol, Generic +from dataclasses import dataclass + +C = TypeVar("C", bound="Comparable") + +class Comparable(Protocol): + pass + +V = TypeVar("V", bound=Comparable) + +@dataclass(slots=True) # Explicit slots are still not ok: +class Node(Generic[V]): # E: "Node" both defines "__slots__" and is used with "slots=True" + __slots__ = ('data',) + data: V +[builtins fixtures/dataclasses.pyi] From 081ea85d91637d65550d92f39c0874bc3982cda0 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Wed, 22 Dec 2021 23:44:31 +0300 Subject: [PATCH 2/3] Improves test --- test-data/unit/check-dataclasses.test | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index 7e6f55f7a863..a654c23aafad 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1493,3 +1493,21 @@ class Node(Generic[V]): # E: "Node" both defines "__slots__" and is used with " __slots__ = ('data',) data: V [builtins fixtures/dataclasses.pyi] + +[case testSlotsDefinitionWithTwoPasses3] +# flags: --python-version 3.10 +from typing import TypeVar, Protocol, Generic +from dataclasses import dataclass + +C = TypeVar("C", bound="Comparable") + +class Comparable(Protocol): + pass + +V = TypeVar("V", bound=Comparable) + +@dataclass(slots=True) # Explicit slots are still not ok, even empty ones: +class Node(Generic[V]): # E: "Node" both defines "__slots__" and is used with "slots=True" + __slots__ = () + data: V +[builtins fixtures/dataclasses.pyi] From 92cce9a6ba3d9420c7df7587d52f1999c1d25068 Mon Sep 17 00:00:00 2001 From: sobolevn Date: Thu, 23 Dec 2021 10:17:01 +0300 Subject: [PATCH 3/3] More tests --- test-data/unit/check-dataclasses.test | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test-data/unit/check-dataclasses.test b/test-data/unit/check-dataclasses.test index a654c23aafad..80e5f81e110f 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1511,3 +1511,14 @@ class Node(Generic[V]): # E: "Node" both defines "__slots__" and is used with " __slots__ = () data: V [builtins fixtures/dataclasses.pyi] + +[case testSlotsDefinitionWithTwoPasses4] +# flags: --python-version 3.10 +import dataclasses as dtc + +PublishedMessagesVar = dict[int, 'PublishedMessages'] + +@dtc.dataclass(frozen=True, slots=True) +class PublishedMessages: + left: int +[builtins fixtures/dataclasses.pyi]