From 9f0dd79eb075476516b721785e97706f5134cb4c Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Mon, 12 Jan 2026 16:52:05 -0800 Subject: [PATCH 1/2] Fix slots issue with deferred base Fixes #17121 Generated by codex --- mypy/plugins/dataclasses.py | 15 ++++++++++++--- test-data/unit/check-dataclasses.test | 14 ++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index e916ded01dd2..b6c58366a7ba 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -458,8 +458,11 @@ def add_slots( return generated_slots = {attr.name for attr in attributes} - if (info.slots is not None and info.slots != generated_slots) or info.names.get( - "__slots__" + existing_slots = info.names.get("__slots__") + slots_defined_by_plugin = existing_slots is not None and existing_slots.plugin_generated + if ( + (info.slots is not None and info.slots != generated_slots and not slots_defined_by_plugin) + or (existing_slots is not None and not slots_defined_by_plugin) ): # This means we have a slots conflict. # Class explicitly specifies a different `__slots__` field. @@ -485,7 +488,13 @@ def add_slots( [self._api.named_type("builtins.str") for _ in generated_slots], self._api.named_type("builtins.tuple"), ) - add_attribute_to_class(self._api, self._cls, "__slots__", slots_type) + add_attribute_to_class( + self._api, + self._cls, + "__slots__", + slots_type, + overwrite_existing=slots_defined_by_plugin, + ) 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 3cc4a03ffb11..b12d12393ff5 100644 --- a/test-data/unit/check-dataclasses.test +++ b/test-data/unit/check-dataclasses.test @@ -1606,6 +1606,20 @@ class PublishedMessages: left: int [builtins fixtures/dataclasses.pyi] +[case testDataclassSlotsWithDeferredBase] +# flags: --python-version 3.10 +from dataclasses import dataclass + +@dataclass +class A(B): ... + +@dataclass +class B: ... + +@dataclass(slots=True) +class C: ... +[builtins fixtures/dataclasses.pyi] + [case testDataclassesAnyInherit] from dataclasses import dataclass from typing import Any From b81647ebed1a29ec823f60c16f0a9deeefcdf3b7 Mon Sep 17 00:00:00 2001 From: hauntsaninja Date: Mon, 12 Jan 2026 16:56:52 -0800 Subject: [PATCH 2/2] simplify --- mypy/plugins/dataclasses.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/mypy/plugins/dataclasses.py b/mypy/plugins/dataclasses.py index b6c58366a7ba..97faadbad064 100644 --- a/mypy/plugins/dataclasses.py +++ b/mypy/plugins/dataclasses.py @@ -457,13 +457,9 @@ def add_slots( ) return - generated_slots = {attr.name for attr in attributes} existing_slots = info.names.get("__slots__") slots_defined_by_plugin = existing_slots is not None and existing_slots.plugin_generated - if ( - (info.slots is not None and info.slots != generated_slots and not slots_defined_by_plugin) - or (existing_slots is not None and not slots_defined_by_plugin) - ): + if existing_slots is not None and not slots_defined_by_plugin: # This means we have a slots conflict. # Class explicitly specifies a different `__slots__` field. # And `@dataclass(slots=True)` is used. @@ -481,6 +477,7 @@ def add_slots( # does not have concrete `__slots__` defined. Ignoring. return + generated_slots = {attr.name for attr in attributes} info.slots = generated_slots # Now, insert `.__slots__` attribute to class namespace: