Commit ed7d429e authored by Eric V. Smith's avatar Eric V. Smith Committed by GitHub

bpo-32278: Allow dataclasses.make_dataclass() to omit type information. (gh-5115)

parent e7ba013d
...@@ -708,9 +708,10 @@ def _astuple_inner(obj, tuple_factory): ...@@ -708,9 +708,10 @@ def _astuple_inner(obj, tuple_factory):
def make_dataclass(cls_name, fields, *, bases=(), namespace=None): def make_dataclass(cls_name, fields, *, bases=(), namespace=None):
"""Return a new dynamically created dataclass. """Return a new dynamically created dataclass.
The dataclass name will be 'cls_name'. 'fields' is an interable The dataclass name will be 'cls_name'. 'fields' is an iterable
of either (name, type) or (name, type, Field) objects. Field of either (name), (name, type) or (name, type, Field) objects. If type is
objects are created by calling 'field(name, type [, Field])'. 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) 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): ...@@ -730,12 +731,19 @@ def make_dataclass(cls_name, fields, *, bases=(), namespace=None):
# Copy namespace since we're going to mutate it. # Copy namespace since we're going to mutate it.
namespace = namespace.copy() namespace = namespace.copy()
anns = collections.OrderedDict((name, tp) for name, tp, *_ in fields) anns = collections.OrderedDict()
namespace['__annotations__'] = anns
for item in fields: 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 name, tp, spec = item
namespace[name] = spec namespace[name] = spec
anns[name] = tp
namespace['__annotations__'] = anns
cls = type(cls_name, bases, namespace) cls = type(cls_name, bases, namespace)
return dataclass(cls) return dataclass(cls)
......
...@@ -2033,6 +2033,20 @@ class TestCase(unittest.TestCase): ...@@ -2033,6 +2033,20 @@ class TestCase(unittest.TestCase):
self.assertEqual(C.y, 10) self.assertEqual(C.y, 10)
self.assertEqual(C.z, 20) 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): class TestDocString(unittest.TestCase):
def assertDocStrEqual(self, a, b): def assertDocStrEqual(self, a, b):
......
Make type information optional on dataclasses.make_dataclass(). If omitted,
the string 'typing.Any' is used.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment