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

This commit is contained in:
Eric V. Smith 2018-01-06 16:14:03 -05:00 committed by GitHub
parent e7ba013d87
commit ed7d429ebb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 30 additions and 6 deletions

View File

@ -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):
# 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)

View File

@ -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):

View File

@ -0,0 +1,2 @@
Make type information optional on dataclasses.make_dataclass(). If omitted,
the string 'typing.Any' is used.