diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index bd317aa9b0f2f37..77af910b4ad2c44 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -561,8 +561,7 @@ def __annotate__(format, /): case _: raise NotImplementedError(format) - # This is a flag for _add_slots to know it needs to regenerate this method - # In order to remove references to the original class when it is replaced + # Mark the function as generated by dataclasses, for introspection. __annotate__.__generated_by_dataclasses__ = True __annotate__.__qualname__ = f"{__class__.__qualname__}.{method_name}.__annotate__" @@ -1118,6 +1117,16 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, # also marks this class as being a dataclass. setattr(cls, _FIELDS, fields) + # It's an error to specify weakref_slot if slots is False. + if weakref_slot and not slots: + raise TypeError('weakref_slot is True but slots is False') + if slots: + # Create the slotted class before generating any methods, so that + # the generated methods are bound to the final class from the start + # and the __class__ cell fix-up in _add_slots only has to look at + # user-defined methods. + cls = _add_slots(cls, frozen, weakref_slot, fields) + # Was this class defined with an explicit __hash__? Note that if # __eq__ is defined in this class, then python will automatically # set __hash__ to None. This is a heuristic, as it's possible @@ -1235,12 +1244,6 @@ def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen, _set_new_attribute(cls, '__match_args__', tuple(f.name for f in std_init_fields)) - # It's an error to specify weakref_slot if slots is False. - if weakref_slot and not slots: - raise TypeError('weakref_slot is True but slots is False') - if slots: - cls = _add_slots(cls, frozen, weakref_slot, fields) - abc.update_abstractmethods(cls) return cls @@ -1418,12 +1421,6 @@ def _add_slots(cls, is_frozen, weakref_slot, defined_fields): else: f.type = ann - # Fix the class reference in the __annotate__ method - init = newcls.__init__ - if init_annotate := getattr(init, "__annotate__", None): - if getattr(init_annotate, "__generated_by_dataclasses__", False): - _update_func_cell_for__class__(init_annotate, cls, newcls) - return newcls diff --git a/Lib/test/test_dataclasses/__init__.py b/Lib/test/test_dataclasses/__init__.py index a89999bb97938c0..3a895b1b0b15324 100644 --- a/Lib/test/test_dataclasses/__init__.py +++ b/Lib/test/test_dataclasses/__init__.py @@ -41,6 +41,22 @@ def test_lazy_import(self): "dataclasses", {"inspect", "re", "copy"} ) + @cpython_only + def test_slots_does_not_import_inspect(self): + # inspect is only needed to unwrap user-defined methods + # that are wrapped by a decorator. + create_slotted_class = textwrap.dedent( + """ + @dataclasses.dataclass(slots=True) + class C: + x: int = 0 + """ + ) + import_helper.ensure_lazy_imports( + "dataclasses", {"inspect"}, + additional_code=create_slotted_class, + ) + class TestCase(unittest.TestCase): def test_no_fields(self): diff --git a/Misc/NEWS.d/next/Library/2026-09-03-00-42-32.gh-issue-154675.NX8eYm.rst b/Misc/NEWS.d/next/Library/2026-09-03-00-42-32.gh-issue-154675.NX8eYm.rst new file mode 100644 index 000000000000000..0cb6c2174b0dfb9 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-09-03-00-42-32.gh-issue-154675.NX8eYm.rst @@ -0,0 +1,3 @@ +Creating a :func:`~dataclasses.dataclass` with ``slots=True`` no longer +imports the :mod:`inspect` module, unless a user-defined method is wrapped +by a decorator.