https://github.com/python/cpython/commit/3e3a4d231518f91ff2f3c5a085b3849e32f1d548
commit: 3e3a4d231518f91ff2f3c5a085b3849e32f1d548
branch: main
author: Victorien <[email protected]>
committer: ericvsmith <[email protected]>
date: 2024-10-01T09:51:51-04:00
summary:

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.

files:
A Misc/NEWS.d/next/Library/2024-08-06-07-24-00.gh-issue-118974.qamsCQ.rst
M Doc/library/dataclasses.rst
M Lib/dataclasses.py
M Lib/test/test_dataclasses/__init__.py

diff --git a/Doc/library/dataclasses.rst b/Doc/library/dataclasses.rst
index 51c1a427b63787..e34b2db0210960 100644
--- a/Doc/library/dataclasses.rst
+++ b/Doc/library/dataclasses.rst
@@ -399,7 +399,7 @@ Module contents
    :func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass
    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
    in *fields*, base classes as given in *bases*, and initialized
@@ -415,6 +415,11 @@ Module contents
    of the dataclass is set to that value.
    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
    mechanism for creating a new class with :attr:`!__annotations__` can
    then apply the :func:`@dataclass <dataclass>` function to convert that 
class to
@@ -438,6 +443,9 @@ Module contents
          def add_one(self):
              return self.x + 1
 
+   .. versionadded:: 3.14
+      Added the *decorator* parameter.
+
 .. function:: replace(obj, /, **changes)
 
    Creates a new object of the same type as *obj*, replacing
diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py
index bdda7cc6c00f5d..7a24f8a9e5ccee 100644
--- a/Lib/dataclasses.py
+++ b/Lib/dataclasses.py
@@ -1550,7 +1550,7 @@ def _astuple_inner(obj, tuple_factory):
 def 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):
+                   weakref_slot=False, module=None, decorator=dataclass):
     """Return a new dynamically created dataclass.
 
     The dataclass name will be 'cls_name'.  'fields' is an iterable
@@ -1630,8 +1630,8 @@ def exec_body_callback(ns):
     if module is not None:
         cls.__module__ = module
 
-    # Apply the normal decorator.
-    return dataclass(cls, init=init, repr=repr, eq=eq, order=order,
+    # Apply the normal provided decorator.
+    return decorator(cls, init=init, repr=repr, eq=eq, order=order,
                      unsafe_hash=unsafe_hash, frozen=frozen,
                      match_args=match_args, kw_only=kw_only, slots=slots,
                      weakref_slot=weakref_slot)
diff --git a/Lib/test/test_dataclasses/__init__.py 
b/Lib/test/test_dataclasses/__init__.py
index 2984f4261bd2c4..2e6c49e29ce828 100644
--- a/Lib/test/test_dataclasses/__init__.py
+++ b/Lib/test/test_dataclasses/__init__.py
@@ -4317,6 +4317,23 @@ def test_funny_class_names_names(self):
                 C = make_dataclass(classname, ['a', 'b'])
                 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):
     def test(self):
         @dataclass(frozen=True)
diff --git 
a/Misc/NEWS.d/next/Library/2024-08-06-07-24-00.gh-issue-118974.qamsCQ.rst 
b/Misc/NEWS.d/next/Library/2024-08-06-07-24-00.gh-issue-118974.qamsCQ.rst
new file mode 100644
index 00000000000000..79480a69c1a90e
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-08-06-07-24-00.gh-issue-118974.qamsCQ.rst
@@ -0,0 +1,2 @@
+Add ``decorator`` parameter to :func:`dataclasses.make_dataclass`
+to customize the functional creation of dataclasses.

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/python-checkins.python.org/
Member address: [email protected]

Reply via email to