From 5a7092de1226a95a50f0f384eea8ddb288959249 Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sat, 31 Mar 2018 13:41:17 +0100 Subject: [PATCH] Allow dynamic creation of generic dataclasses (GH-6319) --- Lib/dataclasses.py | 4 +++- Lib/test/test_dataclasses.py | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Lib/dataclasses.py b/Lib/dataclasses.py index bd7252c683c..04e07f8cf8c 100644 --- a/Lib/dataclasses.py +++ b/Lib/dataclasses.py @@ -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) diff --git a/Lib/test/test_dataclasses.py b/Lib/test/test_dataclasses.py index 5cd424cf576..26bfc4e75a0 100755 --- a/Lib/test/test_dataclasses.py +++ b/Lib/test/test_dataclasses.py @@ -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: