Skip to content

Commit 3e3a4d2

Browse files
authored
pythongh-118974: Add decorator argument to make_dataclass (pythongh-122723)
This is to allow the `dataclasses.make_dataclass` infrastructure to be used with another decorator that's compliant with `typing.dataclass_transform`. The new `decorator` argument to `dataclasses.make_dataclass` is `dataclasses.dataclass`, which used to be hard coded.
1 parent 91e64be commit 3e3a4d2

File tree

4 files changed

+31
-4
lines changed

4 files changed

+31
-4
lines changed

Doc/library/dataclasses.rst

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ Module contents
399399
:func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass
400400
instance.
401401

402-
.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None)
402+
.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, decorator=dataclass)
403403

404404
Creates a new dataclass with name *cls_name*, fields as defined
405405
in *fields*, base classes as given in *bases*, and initialized
@@ -415,6 +415,11 @@ Module contents
415415
of the dataclass is set to that value.
416416
By default, it is set to the module name of the caller.
417417

418+
The *decorator* parameter is a callable that will be used to create the dataclass.
419+
It should take the class object as a first argument and the same keyword arguments
420+
as :func:`@dataclass <dataclass>`. By default, the :func:`@dataclass <dataclass>`
421+
function is used.
422+
418423
This function is not strictly required, because any Python
419424
mechanism for creating a new class with :attr:`!__annotations__` can
420425
then apply the :func:`@dataclass <dataclass>` function to convert that class to
@@ -438,6 +443,9 @@ Module contents
438443
def add_one(self):
439444
return self.x + 1
440445

446+
.. versionadded:: 3.14
447+
Added the *decorator* parameter.
448+
441449
.. function:: replace(obj, /, **changes)
442450

443451
Creates a new object of the same type as *obj*, replacing

Lib/dataclasses.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,7 @@ def _astuple_inner(obj, tuple_factory):
15501550
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
15511551
repr=True, eq=True, order=False, unsafe_hash=False,
15521552
frozen=False, match_args=True, kw_only=False, slots=False,
1553-
weakref_slot=False, module=None):
1553+
weakref_slot=False, module=None, decorator=dataclass):
15541554
"""Return a new dynamically created dataclass.
15551555
15561556
The dataclass name will be 'cls_name'. 'fields' is an iterable
@@ -1630,8 +1630,8 @@ def exec_body_callback(ns):
16301630
if module is not None:
16311631
cls.__module__ = module
16321632

1633-
# Apply the normal decorator.
1634-
return dataclass(cls, init=init, repr=repr, eq=eq, order=order,
1633+
# Apply the normal provided decorator.
1634+
return decorator(cls, init=init, repr=repr, eq=eq, order=order,
16351635
unsafe_hash=unsafe_hash, frozen=frozen,
16361636
match_args=match_args, kw_only=kw_only, slots=slots,
16371637
weakref_slot=weakref_slot)

Lib/test/test_dataclasses/__init__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4317,6 +4317,23 @@ def test_funny_class_names_names(self):
43174317
C = make_dataclass(classname, ['a', 'b'])
43184318
self.assertEqual(C.__name__, classname)
43194319

4320+
def test_dataclass_decorator_default(self):
4321+
C = make_dataclass('C', [('x', int)], decorator=dataclass)
4322+
c = C(10)
4323+
self.assertEqual(c.x, 10)
4324+
4325+
def test_dataclass_custom_decorator(self):
4326+
def custom_dataclass(cls, *args, **kwargs):
4327+
dc = dataclass(cls, *args, **kwargs)
4328+
dc.__custom__ = True
4329+
return dc
4330+
4331+
C = make_dataclass('C', [('x', int)], decorator=custom_dataclass)
4332+
c = C(10)
4333+
self.assertEqual(c.x, 10)
4334+
self.assertEqual(c.__custom__, True)
4335+
4336+
43204337
class TestReplace(unittest.TestCase):
43214338
def test(self):
43224339
@dataclass(frozen=True)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add ``decorator`` parameter to :func:`dataclasses.make_dataclass`
2+
to customize the functional creation of dataclasses.

0 commit comments

Comments
 (0)