]> granicus.if.org Git - python/commitdiff
Allow dynamic creation of generic dataclasses (GH-6319) (GH-6320)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sun, 1 Apr 2018 11:33:13 +0000 (04:33 -0700)
committerIvan Levkivskyi <levkivskyi@gmail.com>
Sun, 1 Apr 2018 11:33:13 +0000 (12:33 +0100)
(cherry picked from commit 5a7092de1226a95a50f0f384eea8ddb288959249)

Co-authored-by: Ivan Levkivskyi <levkivskyi@gmail.com>
Lib/dataclasses.py
Lib/test/test_dataclasses.py

index bd7252c683cabfa7b6d55853f6a08ace1a22c7d7..04e07f8cf8c2703f93909dec5ff47bba0fedfa85 100644 (file)
@@ -1004,7 +1004,9 @@ def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
         anns[name] = tp
 
     namespace['__annotations__'] = anns
-    cls = type(cls_name, bases, namespace)
+    # We use `types.new_class()` instead of simply `type()` to allow dynamic creation
+    # of generic dataclassses.
+    cls = types.new_class(cls_name, bases, {}, lambda ns: ns.update(namespace))
     return dataclass(cls, init=init, repr=repr, eq=eq, order=order,
                      unsafe_hash=unsafe_hash, frozen=frozen)
 
index 5cd424cf5760e69751b794a2e5bdf58fb1432e8d..26bfc4e75a001e04ad6d87f2d2b5dc0418dbe8f2 100755 (executable)
@@ -8,7 +8,7 @@ import pickle
 import inspect
 import unittest
 from unittest.mock import Mock
-from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar
+from typing import ClassVar, Any, List, Union, Tuple, Dict, Generic, TypeVar, Optional
 from collections import deque, OrderedDict, namedtuple
 from functools import total_ordering
 
@@ -1690,6 +1690,23 @@ class TestCase(unittest.TestCase):
         c = Alias(10, 1.0)
         self.assertEqual(c.new_method(), 1.0)
 
+    def test_generic_dynamic(self):
+        T = TypeVar('T')
+
+        @dataclass
+        class Parent(Generic[T]):
+            x: T
+        Child = make_dataclass('Child', [('y', T), ('z', Optional[T], None)],
+                               bases=(Parent[int], Generic[T]), namespace={'other': 42})
+        self.assertIs(Child[int](1, 2).z, None)
+        self.assertEqual(Child[int](1, 2, 3).z, 3)
+        self.assertEqual(Child[int](1, 2, 3).other, 42)
+        # Check that type aliases work correctly.
+        Alias = Child[T]
+        self.assertEqual(Alias[int](1, 2).x, 1)
+        # Check MRO resolution.
+        self.assertEqual(Child.__mro__, (Child, Parent, Generic, object))
+
     def test_helper_replace(self):
         @dataclass(frozen=True)
         class C: