]> granicus.if.org Git - python/commitdiff
bpo-32278: Allow dataclasses.make_dataclass() to omit type information. (gh-5115)
authorEric V. Smith <ericvsmith@users.noreply.github.com>
Sat, 6 Jan 2018 21:14:03 +0000 (16:14 -0500)
committerGitHub <noreply@github.com>
Sat, 6 Jan 2018 21:14:03 +0000 (16:14 -0500)
Lib/dataclasses.py
Lib/test/test_dataclasses.py
Misc/NEWS.d/next/Library/2018-01-06-15-15-34.bpo-32278.bGnGc0.rst [new file with mode: 0644]

index b4786bf502e8c6af69f67744cac656be541484dc..c4e94b8f667381b3c9460ee595117faaf73c3b71 100644 (file)
@@ -708,9 +708,10 @@ def _astuple_inner(obj, tuple_factory):
 def make_dataclass(cls_name, fields, *, bases=(), namespace=None):
     """Return a new dynamically created dataclass.
 
-    The dataclass name will be 'cls_name'.  'fields' is an interable
-    of either (name, type) or (name, type, Field) objects. Field
-    objects are created by calling 'field(name, type [, Field])'.
+    The dataclass name will be 'cls_name'.  'fields' is an iterable
+    of either (name), (name, type) or (name, type, Field) objects. If type is
+    omitted, use the string 'typing.Any'.  Field objects are created by
+    calling 'field(name, type [, Field])'.
 
       C = make_class('C', [('a', int', ('b', int, Field(init=False))], bases=Base)
 
@@ -730,12 +731,19 @@ def make_dataclass(cls_name, fields, *, bases=(), namespace=None):
         # Copy namespace since we're going to mutate it.
         namespace = namespace.copy()
 
-    anns = collections.OrderedDict((name, tp) for name, tp, *_ in fields)
-    namespace['__annotations__'] = anns
+    anns = collections.OrderedDict()
     for item in fields:
-        if len(item) == 3:
+        if isinstance(item, str):
+            name = item
+            tp = 'typing.Any'
+        elif len(item) == 2:
+            name, tp, = item
+        elif len(item) == 3:
             name, tp, spec = item
             namespace[name] = spec
+        anns[name] = tp
+
+    namespace['__annotations__'] = anns
     cls = type(cls_name, bases, namespace)
     return dataclass(cls)
 
index fca384d8c3c06c7daccdd8d19aa7d195ce089bb4..c44c53d039d5bc157a1102bb38a6a9ace8bf20b4 100755 (executable)
@@ -2033,6 +2033,20 @@ class TestCase(unittest.TestCase):
         self.assertEqual(C.y, 10)
         self.assertEqual(C.z, 20)
 
+    def test_helper_make_dataclass_no_types(self):
+        C = make_dataclass('Point', ['x', 'y', 'z'])
+        c = C(1, 2, 3)
+        self.assertEqual(vars(c), {'x': 1, 'y': 2, 'z': 3})
+        self.assertEqual(C.__annotations__, {'x': 'typing.Any',
+                                             'y': 'typing.Any',
+                                             'z': 'typing.Any'})
+
+        C = make_dataclass('Point', ['x', ('y', int), 'z'])
+        c = C(1, 2, 3)
+        self.assertEqual(vars(c), {'x': 1, 'y': 2, 'z': 3})
+        self.assertEqual(C.__annotations__, {'x': 'typing.Any',
+                                             'y': int,
+                                             'z': 'typing.Any'})
 
 class TestDocString(unittest.TestCase):
     def assertDocStrEqual(self, a, b):
diff --git a/Misc/NEWS.d/next/Library/2018-01-06-15-15-34.bpo-32278.bGnGc0.rst b/Misc/NEWS.d/next/Library/2018-01-06-15-15-34.bpo-32278.bGnGc0.rst
new file mode 100644 (file)
index 0000000..c627468
--- /dev/null
@@ -0,0 +1,2 @@
+Make type information optional on dataclasses.make_dataclass(). If omitted,
+the string 'typing.Any' is used.