0
0
mirror of https://github.com/python/cpython.git synced 2024-11-21 21:09:37 +01:00

gh-118974: Add decorator argument to make_dataclass (gh-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.
This commit is contained in:
Victorien 2024-10-01 15:51:51 +02:00 committed by GitHub
parent 91e64be731
commit 3e3a4d2315
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 31 additions and 4 deletions

View File

@ -399,7 +399,7 @@ Module contents
:func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass :func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass
instance. instance.
.. 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) .. 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)
Creates a new dataclass with name *cls_name*, fields as defined Creates a new dataclass with name *cls_name*, fields as defined
in *fields*, base classes as given in *bases*, and initialized in *fields*, base classes as given in *bases*, and initialized
@ -415,6 +415,11 @@ Module contents
of the dataclass is set to that value. of the dataclass is set to that value.
By default, it is set to the module name of the caller. By default, it is set to the module name of the caller.
The *decorator* parameter is a callable that will be used to create the dataclass.
It should take the class object as a first argument and the same keyword arguments
as :func:`@dataclass <dataclass>`. By default, the :func:`@dataclass <dataclass>`
function is used.
This function is not strictly required, because any Python This function is not strictly required, because any Python
mechanism for creating a new class with :attr:`!__annotations__` can mechanism for creating a new class with :attr:`!__annotations__` can
then apply the :func:`@dataclass <dataclass>` function to convert that class to then apply the :func:`@dataclass <dataclass>` function to convert that class to
@ -438,6 +443,9 @@ Module contents
def add_one(self): def add_one(self):
return self.x + 1 return self.x + 1
.. versionadded:: 3.14
Added the *decorator* parameter.
.. function:: replace(obj, /, **changes) .. function:: replace(obj, /, **changes)
Creates a new object of the same type as *obj*, replacing Creates a new object of the same type as *obj*, replacing

View File

@ -1550,7 +1550,7 @@ def _astuple_inner(obj, tuple_factory):
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
repr=True, eq=True, order=False, unsafe_hash=False, repr=True, eq=True, order=False, unsafe_hash=False,
frozen=False, match_args=True, kw_only=False, slots=False, frozen=False, match_args=True, kw_only=False, slots=False,
weakref_slot=False, module=None): weakref_slot=False, module=None, decorator=dataclass):
"""Return a new dynamically created dataclass. """Return a new dynamically created dataclass.
The dataclass name will be 'cls_name'. 'fields' is an iterable The dataclass name will be 'cls_name'. 'fields' is an iterable
@ -1630,8 +1630,8 @@ def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
if module is not None: if module is not None:
cls.__module__ = module cls.__module__ = module
# Apply the normal decorator. # Apply the normal provided decorator.
return dataclass(cls, init=init, repr=repr, eq=eq, order=order, return decorator(cls, init=init, repr=repr, eq=eq, order=order,
unsafe_hash=unsafe_hash, frozen=frozen, unsafe_hash=unsafe_hash, frozen=frozen,
match_args=match_args, kw_only=kw_only, slots=slots, match_args=match_args, kw_only=kw_only, slots=slots,
weakref_slot=weakref_slot) weakref_slot=weakref_slot)

View File

@ -4317,6 +4317,23 @@ class TestMakeDataclass(unittest.TestCase):
C = make_dataclass(classname, ['a', 'b']) C = make_dataclass(classname, ['a', 'b'])
self.assertEqual(C.__name__, classname) self.assertEqual(C.__name__, classname)
def test_dataclass_decorator_default(self):
C = make_dataclass('C', [('x', int)], decorator=dataclass)
c = C(10)
self.assertEqual(c.x, 10)
def test_dataclass_custom_decorator(self):
def custom_dataclass(cls, *args, **kwargs):
dc = dataclass(cls, *args, **kwargs)
dc.__custom__ = True
return dc
C = make_dataclass('C', [('x', int)], decorator=custom_dataclass)
c = C(10)
self.assertEqual(c.x, 10)
self.assertEqual(c.__custom__, True)
class TestReplace(unittest.TestCase): class TestReplace(unittest.TestCase):
def test(self): def test(self):
@dataclass(frozen=True) @dataclass(frozen=True)

View File

@ -0,0 +1,2 @@
Add ``decorator`` parameter to :func:`dataclasses.make_dataclass`
to customize the functional creation of dataclasses.