repr=True, eq=True, order=False, unsafe_hash=False, |
frozen=False, match_args=True, kw_only=False, slots=False, |
weakref_slot=False, module=None): |
"""Return a new dynamically created dataclass. |
|
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 |
the equivalent of calling 'field(name, type [, Field-info])'.:: |
|
C = make_dataclass('C', ['x', ('y', int), ('z', int, field(init=False))], bases=(Base,)) |
|
is equivalent to:: |
|
@dataclass |
class C(Base): |
x: 'typing.Any' |
y: int |
z: int = field(init=False) |
|
For the bases and namespace parameters, see the builtin type() function. |
|
The parameters init, repr, eq, order, unsafe_hash, frozen, match_args, kw_only, |
slots, and weakref_slot are passed to dataclass(). |
|
If module parameter is defined, the '__module__' attribute of the dataclass is |
set to that value. |
""" |
|
if namespace is None: |
namespace = {} |
|
# While we're looking through the field names, validate that they |
# are identifiers, are not keywords, and not duplicates. |
seen = set() |
annotations = {} |
defaults = {} |
for item in fields: |
if isinstance(item, str): |
name = item |
tp = 'typing.Any' |
elif len(item) == 2: |
name, tp, = item |
elif len(item) == 3: |
name, tp, spec = item |
defaults[name] = spec |
else: |
raise TypeError(f'Invalid field: {item!r}') |
|
if not isinstance(name, str) or not name.isidentifier(): |
raise TypeError(f'Field names must be valid identifiers: {name!r}') |
if keyword.iskeyword(name): |
raise TypeError(f'Field names must not be keywords: {name!r}') |
if name in seen: |
raise TypeError(f'Field name duplicated: {name!r}') |
|
seen.add(name) |
annotations[name] = tp |
|
# Update 'ns' with the user-supplied namespace plus our calculated values. |
def exec_body_callback(ns): |
ns.update(namespace) |
ns.update(defaults) |
ns['__annotations__'] = annotations |
|
# We use `types.new_class()` instead of simply `type()` to allow dynamic creation |
# of generic dataclasses. |
cls = types.new_class(cls_name, bases, {}, exec_body_callback) |
|
# For pickling to work, the __module__ variable needs to be set to the frame |
# where the dataclass is created. |
if module is None: |
try: |
module = sys._getframemodulename(1) or '__main__' |
except AttributeError: |
try: |
module = sys._getframe(1).f_globals.get('__name__', '__main__') |
except (AttributeError, ValueError): |
pass |
if module is not None: |
cls.__module__ = module |
|
# Apply the normal decorator. |
return dataclass(cls, init=init, repr=repr, eq=eq, order=order, |
unsafe_hash=unsafe_hash, frozen=frozen, |
match_args=match_args, kw_only=kw_only, slots=slots, |
weakref_slot=weakref_slot) |