openscap_report.dataclasses.dataclasses   F
last analyzed

Complexity

Total Complexity 146

Size/Duplication

Total Lines 1302
Duplicated Lines 2.3 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 508
dl 30
loc 1302
ccs 0
cts 356
cp 0
rs 2
c 0
b 0
f 0
wmc 146

30 Functions

Rating   Name   Duplication   Size   Complexity  
A _tuple_str() 0 10 2
A _set_new_attribute() 0 7 2
A _hash_exception() 0 3 1
A is_dataclass() 0 4 1
A _is_classvar() 0 6 1
A _field_assign() 0 10 2
A astuple() 0 22 2
A asdict() 0 22 2
A _repr_fn() 0 8 1
C make_dataclass() 0 83 11
A fields() 0 18 2
A field() 0 27 3
A dataclass() 0 32 2
A _hash_set_none() 0 2 1
A _is_initvar() 0 4 1
A _create_fn() 0 19 3
A _frozen_get_del_attr() 0 30 2
A _hash_fn() 0 3 1
B _is_type() 0 57 7
A _cmp_fn() 0 13 1
A _is_dataclass_instance() 0 3 1
A _astuple_inner() 15 15 5
C _init_fn() 0 47 10
F _process_class() 0 179 30
A _asdict_inner() 15 15 5
B replace() 0 53 8
A _hash_add() 0 3 2
A _init_param() 0 18 5
F _get_field() 0 87 15
B _field_init() 0 55 6

9 Methods

Rating   Name   Duplication   Size   Complexity  
A _DataclassParams.__repr__() 0 3 1
A _FIELD_BASE.__repr__() 0 2 1
A Field.__repr__() 0 3 1
A _InitVarMeta.__getitem__() 0 2 1
A _DataclassParams.__init__() 0 7 1
A Field.__init__() 0 15 2
A _FIELD_BASE.__init__() 0 2 1
A Field.__set_name__() 0 6 2
A _HAS_DEFAULT_FACTORY_CLASS.__repr__() 0 2 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like openscap_report.dataclasses.dataclasses often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
# pylint: disable=too-many-lines
2
import copy
3
import inspect
4
import keyword
5
import re
6
import sys
7
import types
8
9
__all__ = [
10
    "dataclass",
11
    "field",
12
    "Field",
13
    "FrozenInstanceError",
14
    "InitVar",
15
    "MISSING",
16
    # Helper functions.
17
    "fields",
18
    "asdict",
19
    "astuple",
20
    "make_dataclass",
21
    "replace",
22
    "is_dataclass",
23
]
24
25
# Conditions for adding methods.  The boxes indicate what action the
26
# dataclass decorator takes.  For all of these tables, when I talk
27
# about init=, repr=, eq=, order=, unsafe_hash=, or frozen=, I'm
28
# referring to the arguments to the @dataclass decorator.  When
29
# checking if a dunder method already exists, I mean check for an
30
# entry in the class's __dict__.  I never check to see if an attribute
31
# is defined in a base class.
32
33
# Key:
34
# +=========+=========================================+
35
# + Value   | Meaning                                 |
36
# +=========+=========================================+
37
# | <blank> | No action: no method is added.          |
38
# +---------+-----------------------------------------+
39
# | add     | Generated method is added.              |
40
# +---------+-----------------------------------------+
41
# | raise   | TypeError is raised.                    |
42
# +---------+-----------------------------------------+
43
# | None    | Attribute is set to None.               |
44
# +=========+=========================================+
45
46
# __init__
47
#
48
#   +--- init= parameter
49
#   |
50
#   v     |       |       |
51
#         |  no   |  yes  |  <--- class has __init__ in __dict__?
52
# +=======+=======+=======+
53
# | False |       |       |
54
# +-------+-------+-------+
55
# | True  | add   |       |  <- the default
56
# +=======+=======+=======+
57
58
# __repr__
59
#
60
#    +--- repr= parameter
61
#    |
62
#    v    |       |       |
63
#         |  no   |  yes  |  <--- class has __repr__ in __dict__?
64
# +=======+=======+=======+
65
# | False |       |       |
66
# +-------+-------+-------+
67
# | True  | add   |       |  <- the default
68
# +=======+=======+=======+
69
70
71
# __setattr__
72
# __delattr__
73
#
74
#    +--- frozen= parameter
75
#    |
76
#    v    |       |       |
77
#         |  no   |  yes  |  <--- class has __setattr__ or __delattr__ in __dict__?
78
# +=======+=======+=======+
79
# | False |       |       |  <- the default
80
# +-------+-------+-------+
81
# | True  | add   | raise |
82
# +=======+=======+=======+
83
# Raise because not adding these methods would break the "frozen-ness"
84
# of the class.
85
86
# __eq__
87
#
88
#    +--- eq= parameter
89
#    |
90
#    v    |       |       |
91
#         |  no   |  yes  |  <--- class has __eq__ in __dict__?
92
# +=======+=======+=======+
93
# | False |       |       |
94
# +-------+-------+-------+
95
# | True  | add   |       |  <- the default
96
# +=======+=======+=======+
97
98
# __lt__
99
# __le__
100
# __gt__
101
# __ge__
102
#
103
#    +--- order= parameter
104
#    |
105
#    v    |       |       |
106
#         |  no   |  yes  |  <--- class has any comparison method in __dict__?
107
# +=======+=======+=======+
108
# | False |       |       |  <- the default
109
# +-------+-------+-------+
110
# | True  | add   | raise |
111
# +=======+=======+=======+
112
# Raise because to allow this case would interfere with using
113
# functools.total_ordering.
114
115
# __hash__
116
117
#    +------------------- unsafe_hash= parameter
118
#    |       +----------- eq= parameter
119
#    |       |       +--- frozen= parameter
120
#    |       |       |
121
#    v       v       v    |        |        |
122
#                         |   no   |  yes   |  <--- class has explicitly defined __hash__
123
# +=======+=======+=======+========+========+
124
# | False | False | False |        |        | No __eq__, use the base class __hash__
125
# +-------+-------+-------+--------+--------+
126
# | False | False | True  |        |        | No __eq__, use the base class __hash__
127
# +-------+-------+-------+--------+--------+
128
# | False | True  | False | None   |        | <-- the default, not hashable
129
# +-------+-------+-------+--------+--------+
130
# | False | True  | True  | add    |        | Frozen, so hashable, allows override
131
# +-------+-------+-------+--------+--------+
132
# | True  | False | False | add    | raise  | Has no __eq__, but hashable
133
# +-------+-------+-------+--------+--------+
134
# | True  | False | True  | add    | raise  | Has no __eq__, but hashable
135
# +-------+-------+-------+--------+--------+
136
# | True  | True  | False | add    | raise  | Not frozen, but hashable
137
# +-------+-------+-------+--------+--------+
138
# | True  | True  | True  | add    | raise  | Frozen, so hashable
139
# +=======+=======+=======+========+========+
140
# For boxes that are blank, __hash__ is untouched and therefore
141
# inherited from the base class.  If the base is object, then
142
# id-based hashing is used.
143
#
144
# Note that a class may already have __hash__=None if it specified an
145
# __eq__ method in the class body (not one that was created by
146
# @dataclass).
147
#
148
# See _hash_action (below) for a coded version of this table.
149
150
151
# Raised when an attempt is made to modify a frozen class.
152
class FrozenInstanceError(AttributeError):
153
    pass
154
155
156
# A sentinel object for default values to signal that a default
157
# factory will be used.  This is given a nice repr() which will appear
158
# in the function signature of dataclasses' constructors.
159
# pylint: disable=invalid-name
160
class _HAS_DEFAULT_FACTORY_CLASS:
161
    def __repr__(self):
162
        return "<factory>"
163
164
165
_HAS_DEFAULT_FACTORY = _HAS_DEFAULT_FACTORY_CLASS()
166
167
168
# A sentinel object to detect if a parameter is supplied or not.  Use
169
# a class to give it a better repr.
170
# pylint: disable=invalid-name
171
class _MISSING_TYPE:
172
    pass
173
174
175
MISSING = _MISSING_TYPE()
176
177
# Since most per-field metadata will be unused, create an empty
178
# read-only proxy that can be shared among all fields.
179
_EMPTY_METADATA = types.MappingProxyType({})
180
181
182
# Markers for the various kinds of fields and pseudo-fields.
183
# pylint: disable=invalid-name
184
class _FIELD_BASE:
185
    def __init__(self, name):
186
        self.name = name
187
188
    def __repr__(self):
189
        return self.name
190
191
192
_FIELD = _FIELD_BASE("_FIELD")
193
_FIELD_CLASSVAR = _FIELD_BASE("_FIELD_CLASSVAR")
194
_FIELD_INITVAR = _FIELD_BASE("_FIELD_INITVAR")
195
196
# The name of an attribute on the class where we store the Field
197
# objects.  Also used to check if a class is a Data Class.
198
_FIELDS = "__dataclass_fields__"
199
200
# The name of an attribute on the class that stores the parameters to
201
# @dataclass.
202
_PARAMS = "__dataclass_params__"
203
204
# The name of the function, that if it exists, is called at the end of
205
# __init__.
206
_POST_INIT_NAME = "__post_init__"
207
208
# String regex that string annotations for ClassVar or InitVar must match.
209
# Allows "identifier.identifier[" or "identifier[".
210
# https://bugs.python.org/issue33453 for details.
211
_MODULE_IDENTIFIER_RE = re.compile(r"^(?:\s*(\w+)\s*\.)?\s*(\w+)")
212
213
214
class _InitVarMeta(type):
215
    def __getitem__(cls, params):
216
        return cls
217
218
219
class InitVar(metaclass=_InitVarMeta):
220
    pass
221
222
223
# Instances of Field are only ever created from within this module,
224
# and only from the field() function, although Field instances are
225
# exposed externally as (conceptually) read-only objects.
226
#
227
# name and type are filled in after the fact, not in __init__.
228
# They're not known at the time this class is instantiated, but it's
229
# convenient if they're available later.
230
#
231
# When cls._FIELDS is filled in with a list of Field objects, the name
232
# and type fields will have been populated.
233
# pylint: disable=too-many-instance-attributes
234
class Field:
235
    __slots__ = (
236
        "name",
237
        "type",
238
        "default",
239
        "default_factory",
240
        "repr",
241
        "hash",
242
        "init",
243
        "compare",
244
        "metadata",
245
        "_field_type",  # Private: not to be used by user code.
246
    )
247
248
    # pylint: disable=too-many-arguments
249
    # pylint: disable=redefined-builtin
250
    def __init__(self, default, default_factory, init, repr, hash, compare, metadata):
251
        self.name = None
252
        self.type = None
253
        self.default = default
254
        self.default_factory = default_factory
255
        self.init = init
256
        self.repr = repr
257
        self.hash = hash
258
        self.compare = compare
259
        self.metadata = (
260
            _EMPTY_METADATA
261
            if metadata is None or len(metadata) == 0
262
            else types.MappingProxyType(metadata)
263
        )
264
        self._field_type = None
265
266
    def __repr__(self):
267
        return (
268
            "Field("
269
            f"name={self.name!r},"
270
            f"type={self.type!r},"
271
            f"default={self.default!r},"
272
            f"default_factory={self.default_factory!r},"
273
            f"init={self.init!r},"
274
            f"repr={self.repr!r},"
275
            f"hash={self.hash!r},"
276
            f"compare={self.compare!r},"
277
            f"metadata={self.metadata!r},"
278
            f"_field_type={self._field_type}"
279
            ")"
280
        )
281
282
    # This is used to support the PEP 487 __set_name__ protocol in the
283
    # case where we're using a field that contains a descriptor as a
284
    # default value.  For details on __set_name__, see
285
    # https://www.python.org/dev/peps/pep-0487/#implementation-details.
286
    #
287
    # Note that in _process_class, this Field object is overwritten
288
    # with the default value, so the end result is a descriptor that
289
    # had __set_name__ called on it at the right time.
290
    def __set_name__(self, owner, name):
291
        func = getattr(type(self.default), "__set_name__", None)
292
        if func:
293
            # There is a __set_name__ method on the descriptor, call
294
            # it.
295
            func(self.default, owner, name)
296
297
298
class _DataclassParams:
299
    __slots__ = (
300
        "init",
301
        "repr",
302
        "eq",
303
        "order",
304
        "unsafe_hash",
305
        "frozen",
306
    )
307
308
    # pylint: disable=too-many-arguments
309
    # pylint: disable=redefined-builtin
310
    def __init__(self, init, repr, eq, order, unsafe_hash, frozen):
311
        self.init = init
312
        self.repr = repr
313
        self.eq = eq
314
        self.order = order
315
        self.unsafe_hash = unsafe_hash
316
        self.frozen = frozen
317
318
    def __repr__(self):
319
        return (
320
            "_DataclassParams("
321
            f"init={self.init!r},"
322
            f"repr={self.repr!r},"
323
            f"eq={self.eq!r},"
324
            f"order={self.order!r},"
325
            f"unsafe_hash={self.unsafe_hash!r},"
326
            f"frozen={self.frozen!r}"
327
            ")"
328
        )
329
330
331
# This function is used instead of exposing Field creation directly,
332
# so that a type checker can be told (via overloads) that this is a
333
# function whose type depends on its parameters.
334
# pylint: disable=too-many-arguments
335
# pylint: disable=redefined-builtin
336
def field(
337
    *,
338
    default=MISSING,
339
    default_factory=MISSING,
340
    init=True,
341
    repr=True,
342
    hash=None,
343
    compare=True,
344
    metadata=None,
345
):
346
    """Return an object to identify dataclass fields.
347
348
    default is the default value of the field.  default_factory is a
349
    0-argument function called to initialize a field's value.  If init
350
    is True, the field will be a parameter to the class's __init__()
351
    function.  If repr is True, the field will be included in the
352
    object's repr().  If hash is True, the field will be included in
353
    the object's hash().  If compare is True, the field will be used
354
    in comparison functions.  metadata, if specified, must be a
355
    mapping which is stored but not otherwise examined by dataclass.
356
357
    It is an error to specify both default and default_factory.
358
    """
359
360
    if default is not MISSING and default_factory is not MISSING:
361
        raise ValueError("cannot specify both default and default_factory")
362
    return Field(default, default_factory, init, repr, hash, compare, metadata)
363
364
365
# pylint: disable=redefined-outer-name
366
def _tuple_str(obj_name, fields):
367
    # Return a string representing each field of obj_name as a tuple
368
    # member.  So, if fields is ['x', 'y'] and obj_name is "self",
369
    # return "(self.x,self.y)".
370
371
    # Special case for the 0-tuple.
372
    if not fields:
373
        return "()"
374
    # Note the trailing comma, needed if this turns out to be a 1-tuple.
375
    return f'({",".join([f"{obj_name}.{f.name}" for f in fields])},)'
376
377
378
def _create_fn(name, args, body, *, globals=None, locals=None, return_type=MISSING):
379
    # Note that we mutate locals when exec() is called.  Caller
380
    # beware!  The only callers are internal to this module, so no
381
    # worries about external callers.
382
    if locals is None:
383
        locals = {}
384
    return_annotation = ""
385
    if return_type is not MISSING:
386
        locals["_return_type"] = return_type
387
        return_annotation = "->_return_type"
388
    args = ",".join(args)
389
    body = "\n".join(f" {b}" for b in body)
390
391
    # Compute the text of the entire function.
392
    txt = f"def {name}({args}){return_annotation}:\n{body}"
393
394
    # pylint: disable=exec-used
395
    exec(txt, globals, locals)
396
    return locals[name]
397
398
399
def _field_assign(frozen, name, value, self_name):
400
    # If we're a frozen class, then assign to our fields in __init__
401
    # via object.__setattr__.  Otherwise, just use a simple
402
    # assignment.
403
    #
404
    # self_name is what "self" is called in this function: don't
405
    # hard-code "self", since that might be a field name.
406
    if frozen:
407
        return f"object.__setattr__({self_name},{name!r},{value})"
408
    return f"{self_name}.{name}={value}"
409
410
411
def _field_init(f, frozen, globals, self_name):
412
    # Return the text of the line in the body of __init__ that will
413
    # initialize this field.
414
415
    default_name = f"_dflt_{f.name}"
416
    if f.default_factory is not MISSING:
417
        if f.init:
418
            # This field has a default factory.  If a parameter is
419
            # given, use it.  If not, call the factory.
420
            globals[default_name] = f.default_factory
421
            value = (
422
                f"{default_name}() "
423
                f"if {f.name} is _HAS_DEFAULT_FACTORY "
424
                f"else {f.name}"
425
            )
426
        else:
427
            # This is a field that's not in the __init__ params, but
428
            # has a default factory function.  It needs to be
429
            # initialized here by calling the factory function,
430
            # because there's no other way to initialize it.
431
432
            # For a field initialized with a default=defaultvalue, the
433
            # class dict just has the default value
434
            # (cls.fieldname=defaultvalue).  But that won't work for a
435
            # default factory, the factory must be called in __init__
436
            # and we must assign that to self.fieldname.  We can't
437
            # fall back to the class dict's value, both because it's
438
            # not set, and because it might be different per-class
439
            # (which, after all, is why we have a factory function!).
440
441
            globals[default_name] = f.default_factory
442
            value = f"{default_name}()"
443
    else:
444
        # No default factory.
445
        if f.init:
446
            if f.default is MISSING:
447
                # There's no default, just do an assignment.
448
                value = f.name
449
            else:
450
                globals[default_name] = f.default
451
                value = f.name
452
        else:
453
            # This field does not need initialization.  Signify that
454
            # to the caller by returning None.
455
            return None
456
457
    # Only test this now, so that we can create variables for the
458
    # default.  However, return None to signify that we're not going
459
    # to actually do the assignment statement for InitVars.
460
    # pylint: disable=protected-access
461
    if f._field_type is _FIELD_INITVAR:
462
        return None
463
464
    # Now, actually generate the field assignment.
465
    return _field_assign(frozen, f.name, value, self_name)
466
467
468
def _init_param(f):
469
    # Return the __init__ parameter string for this field.  For
470
    # example, the equivalent of 'x:int=3' (except instead of 'int',
471
    # reference a variable set to int, and instead of '3', reference a
472
    # variable set to 3).
473
    default = None
474
    if f.default is MISSING and f.default_factory is MISSING:
475
        # There's no default, and no default_factory, just output the
476
        # variable name and type.
477
        default = ""
478
    elif f.default is not MISSING:
479
        # There's a default, this will be the name that's used to look
480
        # it up.
481
        default = f"=_dflt_{f.name}"
482
    elif f.default_factory is not MISSING:
483
        # There's a factory function.  Set a marker.
484
        default = "=_HAS_DEFAULT_FACTORY"
485
    return f"{f.name}:_type_{f.name}{default}"
486
487
488
def _init_fn(fields, frozen, has_post_init, self_name):
489
    # fields contains both real fields and InitVar pseudo-fields.
490
491
    # Make sure we don't have fields without defaults following fields
492
    # with defaults.  This actually would be caught when exec-ing the
493
    # function source code, but catching it here gives a better error
494
    # message, and future-proofs us in case we build up the function
495
    # using ast.
496
    seen_default = False
497
    for f in fields:
498
        # Only consider fields in the __init__ call.
499
        if f.init:
500
            if not (f.default is MISSING and f.default_factory is MISSING):
501
                seen_default = True
502
            elif seen_default:
503
                raise TypeError(
504
                    f"non-default argument {f.name!r} " "follows default argument"
505
                )
506
507
    globals = {"MISSING": MISSING, "_HAS_DEFAULT_FACTORY": _HAS_DEFAULT_FACTORY}
508
509
    body_lines = []
510
    for f in fields:
511
        line = _field_init(f, frozen, globals, self_name)
512
        # line is None means that this field doesn't require
513
        # initialization (it's a pseudo-field).  Just skip it.
514
        if line:
515
            body_lines.append(line)
516
517
    # Does this class have a post-init function?
518
    if has_post_init:
519
        # pylint: disable=protected-access
520
        params_str = ",".join(f.name for f in fields if f._field_type is _FIELD_INITVAR)
521
        body_lines.append(f"{self_name}.{_POST_INIT_NAME}({params_str})")
522
523
    # If no body lines, use 'pass'.
524
    if not body_lines:
525
        body_lines = ["pass"]
526
527
    locals = {f"_type_{f.name}": f.type for f in fields}
528
    return _create_fn(
529
        "__init__",
530
        [self_name] + [_init_param(f) for f in fields if f.init],
531
        body_lines,
532
        locals=locals,
533
        globals=globals,
534
        return_type=None,
535
    )
536
537
538
def _repr_fn(fields):
539
    return _create_fn(
540
        "__repr__",
541
        ("self",),
542
        [
543
            'return self.__class__.__qualname__ + f"('
544
            + ", ".join([f"{f.name}={{self.{f.name}!r}}" for f in fields])  # noqa: W503
545
            + ')"'  # noqa: W503
546
        ],
547
    )
548
549
550
def _frozen_get_del_attr(cls, fields):
551
    # pylint: disable=fixme
552
    # XXX: globals is modified on the first call to _create_fn, then
553
    # the modified version is used in the second call.  Is this okay?
554
    globals = {"cls": cls, "FrozenInstanceError": FrozenInstanceError}
555
    if fields:
556
        fields_str = "(" + ",".join(repr(f.name) for f in fields) + ",)"
557
    else:
558
        # Special case for the zero-length tuple.
559
        fields_str = "()"
560
    return (
561
        _create_fn(
562
            "__setattr__",
563
            ("self", "name", "value"),
564
            (
565
                f"if type(self) is cls or name in {fields_str}:",
566
                ' raise FrozenInstanceError(f"cannot assign to field {name!r}")',
567
                "super(cls, self).__setattr__(name, value)",
568
            ),
569
            globals=globals,
570
        ),
571
        _create_fn(
572
            "__delattr__",
573
            ("self", "name"),
574
            (
575
                f"if type(self) is cls or name in {fields_str}:",
576
                ' raise FrozenInstanceError(f"cannot delete field {name!r}")',
577
                "super(cls, self).__delattr__(name)",
578
            ),
579
            globals=globals,
580
        ),
581
    )
582
583
584
def _cmp_fn(name, op, self_tuple, other_tuple):
585
    # Create a comparison function.  If the fields in the object are
586
    # named 'x' and 'y', then self_tuple is the string
587
    # '(self.x,self.y)' and other_tuple is the string
588
    # '(other.x,other.y)'.
589
590
    return _create_fn(
591
        name,
592
        ("self", "other"),
593
        [
594
            "if other.__class__ is self.__class__:",
595
            f" return {self_tuple}{op}{other_tuple}",
596
            "return NotImplemented",
597
        ],
598
    )
599
600
601
def _hash_fn(fields):
602
    self_tuple = _tuple_str("self", fields)
603
    return _create_fn("__hash__", ("self",), [f"return hash({self_tuple})"])
604
605
606
def _is_classvar(a_type, typing):
607
    # This test uses a typing internal class, but it's the best way to
608
    # test if this is a ClassVar.
609
    # pylint: disable=protected-access
610
    # pylint: disable=unidiomatic-typecheck
611
    return type(a_type) is typing._ClassVar
612
613
614
def _is_initvar(a_type, dataclasses):
615
    # The module we're checking against is the module we're
616
    # currently in (dataclasses.py).
617
    return a_type is dataclasses.InitVar
618
619
620
def _is_type(annotation, cls, a_module, a_type, is_type_predicate):
621
    # Given a type annotation string, does it refer to a_type in
622
    # a_module?  For example, when checking that annotation denotes a
623
    # ClassVar, then a_module is typing, and a_type is
624
    # typing.ClassVar.
625
626
    # It's possible to look up a_module given a_type, but it involves
627
    # looking in sys.modules (again!), and seems like a waste since
628
    # the caller already knows a_module.
629
630
    # - annotation is a string type annotation
631
    # - cls is the class that this annotation was found in
632
    # - a_module is the module we want to match
633
    # - a_type is the type in that module we want to match
634
    # - is_type_predicate is a function called with (obj, a_module)
635
    #   that determines if obj is of the desired type.
636
637
    # Since this test does not do a local namespace lookup (and
638
    # instead only a module (global) lookup), there are some things it
639
    # gets wrong.
640
641
    # With string annotations, cv0 will be detected as a ClassVar:
642
    #   CV = ClassVar
643
    #   @dataclass
644
    #   class C0:
645
    #     cv0: CV
646
647
    # But in this example cv1 will not be detected as a ClassVar:
648
    #   @dataclass
649
    #   class C1:
650
    #     CV = ClassVar
651
    #     cv1: CV
652
653
    # In C1, the code in this function (_is_type) will look up "CV" in
654
    # the module and not find it, so it will not consider cv1 as a
655
    # ClassVar.  This is a fairly obscure corner case, and the best
656
    # way to fix it would be to eval() the string "CV" with the
657
    # correct global and local namespaces.  However that would involve
658
    # a eval() penalty for every single field of every dataclass
659
    # that's defined.  It was judged not worth it.
660
661
    match = _MODULE_IDENTIFIER_RE.match(annotation)
662
    if match:
663
        ns = None
664
        module_name = match.group(1)
665
        if not module_name:
666
            # No module name, assume the class's module did
667
            # "from .dataclasses import InitVar".
668
            ns = sys.modules.get(cls.__module__).__dict__
669
        else:
670
            # Look up module_name in the class's module.
671
            module = sys.modules.get(cls.__module__)
672
            if module and module.__dict__.get(module_name) is a_module:
673
                ns = sys.modules.get(a_type.__module__).__dict__
674
        if ns and is_type_predicate(ns.get(match.group(2)), a_module):
675
            return True
676
    return False
677
678
679
def _get_field(cls, a_name, a_type):
680
    # Return a Field object for this field name and type.  ClassVars
681
    # and InitVars are also returned, but marked as such (see
682
    # f._field_type).
683
684
    # If the default value isn't derived from Field, then it's only a
685
    # normal default value.  Convert it to a Field().
686
    default = getattr(cls, a_name, MISSING)
687
    if isinstance(default, Field):
688
        f = default
689
    else:
690
        if isinstance(default, types.MemberDescriptorType):
691
            # This is a field in __slots__, so it has no default value.
692
            default = MISSING
693
        f = field(default=default)
694
695
    # Only at this point do we know the name and the type.  Set them.
696
    f.name = a_name
697
    f.type = a_type
698
699
    # Assume it's a normal field until proven otherwise.  We're next
700
    # going to decide if it's a ClassVar or InitVar, everything else
701
    # is just a normal field.
702
    # pylint: disable=protected-access
703
    f._field_type = _FIELD
704
705
    # In addition to checking for actual types here, also check for
706
    # string annotations.  get_type_hints() won't always work for us
707
    # (see https://github.com/python/typing/issues/508 for example),
708
    # plus it's expensive and would require an eval for every string
709
    # annotation.  So, make a best effort to see if this is a ClassVar
710
    # or InitVar using regex's and checking that the thing referenced
711
    # is actually of the correct type.
712
713
    # For the complete discussion, see https://bugs.python.org/issue33453
714
715
    # If typing has not been imported, then it's impossible for any
716
    # annotation to be a ClassVar.  So, only look for ClassVar if
717
    # typing has been imported by any module (not necessarily cls's
718
    # module).
719
    typing = sys.modules.get("typing")
720
    if typing:
721
        if _is_classvar(a_type, typing) or (
722
            isinstance(f.type, str)
723
            and _is_type(f.type, cls, typing, typing.ClassVar, _is_classvar)  # noqa: W503
724
        ):
725
            # pylint: disable=protected-access
726
            f._field_type = _FIELD_CLASSVAR
727
728
    # If the type is InitVar, or if it's a matching string annotation,
729
    # then it's an InitVar.
730
    # pylint: disable=protected-access
731
    if f._field_type is _FIELD:
732
        # The module we're checking against is the module we're
733
        # currently in (dataclasses.py).
734
        dataclasses = sys.modules[__name__]
735
        if _is_initvar(a_type, dataclasses) or (
736
            isinstance(f.type, str)
737
            and _is_type(f.type, cls, dataclasses, dataclasses.InitVar, _is_initvar)  # noqa: W503
738
        ):
739
            # pylint: disable=protected-access
740
            f._field_type = _FIELD_INITVAR
741
742
    # Validations for individual fields.  This is delayed until now,
743
    # instead of in the Field() constructor, since only here do we
744
    # know the field name, which allows for better error reporting.
745
746
    # Special restrictions for ClassVar and InitVar.
747
    # pylint: disable=protected-access
748
    if f._field_type in (_FIELD_CLASSVAR, _FIELD_INITVAR):
749
        if f.default_factory is not MISSING:
750
            raise TypeError(f"field {f.name} cannot have a " "default factory")
751
        # Should I check for other field settings? default_factory
752
        # seems the most serious to check for.  Maybe add others.  For
753
        # example, how about init=False (or really,
754
        # init=<not-the-default-init-value>)?  It makes no sense for
755
        # ClassVar and InitVar to specify init=<anything>.
756
757
    # For real fields, disallow mutable defaults for known types.
758
    # pylint: disable=protected-access
759
    if f._field_type is _FIELD and isinstance(f.default, (list, dict, set)):
760
        raise ValueError(
761
            f"mutable default {type(f.default)} for field "
762
            f"{f.name} is not allowed: use default_factory"
763
        )
764
765
    return f
766
767
768
def _set_new_attribute(cls, name, value):
769
    # Never overwrites an existing attribute.  Returns True if the
770
    # attribute already exists.
771
    if name in cls.__dict__:
772
        return True
773
    setattr(cls, name, value)
774
    return False
775
776
777
# Decide if/how we're going to create a hash function.  Key is
778
# (unsafe_hash, eq, frozen, does-hash-exist).  Value is the action to
779
# take.  The common case is to do nothing, so instead of providing a
780
# function that is a no-op, use None to signify that.
781
782
# pylint: disable=unused-argument
783
def _hash_set_none(cls, fields):
784
    return None
785
786
787
# pylint: disable=unused-argument
788
def _hash_add(cls, fields):
789
    flds = [f for f in fields if (f.compare if f.hash is None else f.hash)]
790
    return _hash_fn(flds)
791
792
793
def _hash_exception(cls, fields):
794
    # Raise an exception.
795
    raise TypeError(f"Cannot overwrite attribute __hash__ " f"in class {cls.__name__}")
796
797
798
#
799
#                +-------------------------------------- unsafe_hash?
800
#                |      +------------------------------- eq?
801
#                |      |      +------------------------ frozen?
802
#                |      |      |      +----------------  has-explicit-hash?
803
#                |      |      |      |
804
#                |      |      |      |        +-------  action
805
#                |      |      |      |        |
806
#                v      v      v      v        v
807
_hash_action = {
808
    (False, False, False, False): None,
809
    (False, False, False, True): None,
810
    (False, False, True, False): None,
811
    (False, False, True, True): None,
812
    (False, True, False, False): _hash_set_none,
813
    (False, True, False, True): None,
814
    (False, True, True, False): _hash_add,
815
    (False, True, True, True): None,
816
    (True, False, False, False): _hash_add,
817
    (True, False, False, True): _hash_exception,
818
    (True, False, True, False): _hash_add,
819
    (True, False, True, True): _hash_exception,
820
    (True, True, False, False): _hash_add,
821
    (True, True, False, True): _hash_exception,
822
    (True, True, True, False): _hash_add,
823
    (True, True, True, True): _hash_exception,
824
}
825
# See https://bugs.python.org/issue32929#msg312829 for an if-statement
826
# version of this table.
827
828
829
# pylint: disable=too-many-locals
830
# pylint: disable=too-many-branches
831
# pylint: disable=too-many-statements
832
def _process_class(cls, init, repr, eq, order, unsafe_hash, frozen):  # noqa: C901
833
    # Now that dicts retain insertion order, there's no reason to use
834
    # an ordered dict.  I am leveraging that ordering here, because
835
    # derived class fields overwrite base class fields, but the order
836
    # is defined by the base class, which is found first.
837
    fields = {}
838
839
    setattr(cls, _PARAMS, _DataclassParams(init, repr, eq, order, unsafe_hash, frozen))
840
841
    # Find our base classes in reverse MRO order, and exclude
842
    # ourselves.  In reversed order so that more derived classes
843
    # override earlier field definitions in base classes.  As long as
844
    # we're iterating over them, see if any are frozen.
845
    any_frozen_base = False
846
    has_dataclass_bases = False
847
    for b in cls.__mro__[-1:0:-1]:
848
        # Only process classes that have been processed by our
849
        # decorator.  That is, they have a _FIELDS attribute.
850
        base_fields = getattr(b, _FIELDS, None)
851
        if base_fields:
852
            has_dataclass_bases = True
853
            for f in base_fields.values():
854
                fields[f.name] = f
855
            if getattr(b, _PARAMS).frozen:
856
                any_frozen_base = True
857
858
    # Annotations that are defined in this class (not in base
859
    # classes).  If __annotations__ isn't present, then this class
860
    # adds no new annotations.  We use this to compute fields that are
861
    # added by this class.
862
    #
863
    # Fields are found from cls_annotations, which is guaranteed to be
864
    # ordered.  Default values are from class attributes, if a field
865
    # has a default.  If the default value is a Field(), then it
866
    # contains additional info beyond (and possibly including) the
867
    # actual default value.  Pseudo-fields ClassVars and InitVars are
868
    # included, despite the fact that they're not real fields.  That's
869
    # dealt with later.
870
    cls_annotations = cls.__dict__.get("__annotations__", {})
871
872
    # Now find fields in our class.  While doing so, validate some
873
    # things, and set the default values (as class attributes) where
874
    # we can.
875
    cls_fields = [_get_field(cls, name, type) for name, type in cls_annotations.items()]
876
    for f in cls_fields:
877
        fields[f.name] = f
878
879
        # If the class attribute (which is the default value for this
880
        # field) exists and is of type 'Field', replace it with the
881
        # real default.  This is so that normal class introspection
882
        # sees a real default value, not a Field.
883
        if isinstance(getattr(cls, f.name, None), Field):
884
            if f.default is MISSING:
885
                # If there's no default, delete the class attribute.
886
                # This happens if we specify field(repr=False), for
887
                # example (that is, we specified a field object, but
888
                # no default value).  Also if we're using a default
889
                # factory.  The class attribute should not be set at
890
                # all in the post-processed class.
891
                delattr(cls, f.name)
892
            else:
893
                setattr(cls, f.name, f.default)
894
895
    # Do we have any Field members that don't also have annotations?
896
    for name, value in cls.__dict__.items():
897
        if isinstance(value, Field) and name not in cls_annotations:
898
            raise TypeError(f"{name!r} is a field but has no type annotation")
899
900
    # Check rules that apply if we are derived from any dataclasses.
901
    if has_dataclass_bases:
902
        # Raise an exception if any of our bases are frozen, but we're not.
903
        if any_frozen_base and not frozen:
904
            raise TypeError("cannot inherit non-frozen dataclass from a frozen one")
905
906
        # Raise an exception if we're frozen, but none of our bases are.
907
        if not any_frozen_base and frozen:
908
            raise TypeError("cannot inherit frozen dataclass from a non-frozen one")
909
910
    # Remember all of the fields on our class (including bases).  This
911
    # also marks this class as being a dataclass.
912
    setattr(cls, _FIELDS, fields)
913
914
    # Was this class defined with an explicit __hash__?  Note that if
915
    # __eq__ is defined in this class, then python will automatically
916
    # set __hash__ to None.  This is a heuristic, as it's possible
917
    # that such a __hash__ == None was not auto-generated, but it
918
    # close enough.
919
    class_hash = cls.__dict__.get("__hash__", MISSING)
920
    has_explicit_hash = not (
921
        class_hash is MISSING or (class_hash is None and "__eq__" in cls.__dict__)
922
    )
923
924
    # If we're generating ordering methods, we must be generating the
925
    # eq methods.
926
    if order and not eq:
927
        raise ValueError("eq must be true if order is true")
928
929
    if init:
930
        # Does this class have a post-init function?
931
        has_post_init = hasattr(cls, _POST_INIT_NAME)
932
933
        # Include InitVars and regular fields (so, not ClassVars).
934
        # pylint: disable=protected-access
935
        flds = [f for f in fields.values() if f._field_type in (_FIELD, _FIELD_INITVAR)]
936
        _set_new_attribute(
937
            cls,
938
            "__init__",
939
            _init_fn(
940
                flds,
941
                frozen,
942
                has_post_init,
943
                # The name to use for the "self"
944
                # param in __init__.  Use "self"
945
                # if possible.
946
                "__dataclass_self__" if "self" in fields else "self",
947
            ),
948
        )
949
950
    # Get the fields as a list, and include only real fields.  This is
951
    # used in all of the following methods.
952
    # pylint: disable=protected-access
953
    field_list = [f for f in fields.values() if f._field_type is _FIELD]
954
955
    if repr:
956
        flds = [f for f in field_list if f.repr]
957
        _set_new_attribute(cls, "__repr__", _repr_fn(flds))
958
959
    if eq:
960
        # Create _eq__ method.  There's no need for a __ne__ method,
961
        # since python will call __eq__ and negate it.
962
        flds = [f for f in field_list if f.compare]
963
        self_tuple = _tuple_str("self", flds)
964
        other_tuple = _tuple_str("other", flds)
965
        _set_new_attribute(
966
            cls, "__eq__", _cmp_fn("__eq__", "==", self_tuple, other_tuple)
967
        )
968
969
    if order:
970
        # Create and set the ordering methods.
971
        flds = [f for f in field_list if f.compare]
972
        self_tuple = _tuple_str("self", flds)
973
        other_tuple = _tuple_str("other", flds)
974
        for name, op in [
975
            ("__lt__", "<"),
976
            ("__le__", "<="),
977
            ("__gt__", ">"),
978
            ("__ge__", ">="),
979
        ]:
980
            if _set_new_attribute(
981
                cls, name, _cmp_fn(name, op, self_tuple, other_tuple)
982
            ):
983
                raise TypeError(
984
                    f"Cannot overwrite attribute {name} "
985
                    f"in class {cls.__name__}. Consider using "
986
                    "functools.total_ordering"
987
                )
988
989
    if frozen:
990
        for fn in _frozen_get_del_attr(cls, field_list):
991
            if _set_new_attribute(cls, fn.__name__, fn):
992
                raise TypeError(
993
                    f"Cannot overwrite attribute {fn.__name__} "
994
                    f"in class {cls.__name__}"
995
                )
996
997
    # Decide if/how we're going to create a hash function.
998
    hash_action = _hash_action[
999
        bool(unsafe_hash), bool(eq), bool(frozen), has_explicit_hash
1000
    ]
1001
    if hash_action:
1002
        # No need to call _set_new_attribute here, since by the time
1003
        # we're here the overwriting is unconditional.
1004
        cls.__hash__ = hash_action(cls, field_list)
1005
1006
    if not getattr(cls, "__doc__"):
1007
        # Create a class doc-string.
1008
        cls.__doc__ = cls.__name__ + str(inspect.signature(cls)).replace(" -> None", "")
1009
1010
    return cls
1011
1012
1013
# _cls should never be specified by keyword, so start it with an
1014
# underscore.  The presence of _cls is used to detect if this
1015
# decorator is being called with parameters or not.
1016
def dataclass(
1017
    _cls=None,
1018
    *,
1019
    init=True,
1020
    repr=True,
1021
    eq=True,
1022
    order=False,
1023
    unsafe_hash=False,
1024
    frozen=False,
1025
):
1026
    """Returns the same class as was passed in, with dunder methods
1027
    added based on the fields defined in the class.
1028
1029
    Examines PEP 526 __annotations__ to determine fields.
1030
1031
    If init is true, an __init__() method is added to the class. If
1032
    repr is true, a __repr__() method is added. If order is true, rich
1033
    comparison dunder methods are added. If unsafe_hash is true, a
1034
    __hash__() method function is added. If frozen is true, fields may
1035
    not be assigned to after instance creation.
1036
    """
1037
1038
    def wrap(cls):
1039
        return _process_class(cls, init, repr, eq, order, unsafe_hash, frozen)
1040
1041
    # See if we're being called as @dataclass or @dataclass().
1042
    if _cls is None:
1043
        # We're called with parens.
1044
        return wrap
1045
1046
    # We're called as @dataclass without parens.
1047
    return wrap(_cls)
1048
1049
1050
def fields(class_or_instance):
1051
    """Return a tuple describing the fields of this dataclass.
1052
1053
    Accepts a dataclass or an instance of one. Tuple elements are of
1054
    type Field.
1055
    """
1056
1057
    # Might it be worth caching this, per class?
1058
    try:
1059
        fields = getattr(class_or_instance, _FIELDS)
1060
    except AttributeError:
1061
        # pylint: disable=raise-missing-from
1062
        raise TypeError("must be called with a dataclass type or instance")
1063
1064
    # Exclude pseudo-fields.  Note that fields is sorted by insertion
1065
    # order, so the order of the tuple is as the fields were defined.
1066
    # pylint: disable=protected-access
1067
    return tuple(f for f in fields.values() if f._field_type is _FIELD)
1068
1069
1070
def _is_dataclass_instance(obj):
1071
    """Returns True if obj is an instance of a dataclass."""
1072
    return not isinstance(obj, type) and hasattr(obj, _FIELDS)
1073
1074
1075
def is_dataclass(obj):
1076
    """Returns True if obj is a dataclass or an instance of a
1077
    dataclass."""
1078
    return hasattr(obj, _FIELDS)
1079
1080
1081
def asdict(obj, *, dict_factory=dict):
1082
    """Return the fields of a dataclass instance as a new dictionary mapping
1083
    field names to field values.
1084
1085
    Example usage:
1086
1087
      @dataclass
1088
      class C:
1089
          x: int
1090
          y: int
1091
1092
      c = C(1, 2)
1093
      assert asdict(c) == {'x': 1, 'y': 2}
1094
1095
    If given, 'dict_factory' will be used instead of built-in dict.
1096
    The function applies recursively to field values that are
1097
    dataclass instances. This will also look into built-in containers:
1098
    tuples, lists, and dicts.
1099
    """
1100
    if not _is_dataclass_instance(obj):
1101
        raise TypeError("asdict() should be called on dataclass instances")
1102
    return _asdict_inner(obj, dict_factory)
1103
1104
1105 View Code Duplication
def _asdict_inner(obj, dict_factory):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1106
    if _is_dataclass_instance(obj):
1107
        result = []
1108
        for f in fields(obj):
1109
            value = _asdict_inner(getattr(obj, f.name), dict_factory)
1110
            result.append((f.name, value))
1111
        return dict_factory(result)
1112
    if isinstance(obj, (list, tuple)):
1113
        return type(obj)(_asdict_inner(v, dict_factory) for v in obj)
1114
    if isinstance(obj, dict):
1115
        return type(obj)(
1116
            (_asdict_inner(k, dict_factory), _asdict_inner(v, dict_factory))
1117
            for k, v in obj.items()
1118
        )
1119
    return copy.deepcopy(obj)
1120
1121
1122
def astuple(obj, *, tuple_factory=tuple):
1123
    """Return the fields of a dataclass instance as a new tuple of field values.
1124
1125
    Example usage::
1126
1127
      @dataclass
1128
      class C:
1129
          x: int
1130
          y: int
1131
1132
    c = C(1, 2)
1133
    assert astuple(c) == (1, 2)
1134
1135
    If given, 'tuple_factory' will be used instead of built-in tuple.
1136
    The function applies recursively to field values that are
1137
    dataclass instances. This will also look into built-in containers:
1138
    tuples, lists, and dicts.
1139
    """
1140
1141
    if not _is_dataclass_instance(obj):
1142
        raise TypeError("astuple() should be called on dataclass instances")
1143
    return _astuple_inner(obj, tuple_factory)
1144
1145
1146 View Code Duplication
def _astuple_inner(obj, tuple_factory):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
1147
    if _is_dataclass_instance(obj):
1148
        result = []
1149
        for f in fields(obj):
1150
            value = _astuple_inner(getattr(obj, f.name), tuple_factory)
1151
            result.append(value)
1152
        return tuple_factory(result)
1153
    if isinstance(obj, (list, tuple)):
1154
        return type(obj)(_astuple_inner(v, tuple_factory) for v in obj)
1155
    if isinstance(obj, dict):
1156
        return type(obj)(
1157
            (_astuple_inner(k, tuple_factory), _astuple_inner(v, tuple_factory))
1158
            for k, v in obj.items()
1159
        )
1160
    return copy.deepcopy(obj)
1161
1162
1163
def make_dataclass(
1164
    cls_name,
1165
    fields,
1166
    *,
1167
    bases=(),
1168
    namespace=None,
1169
    init=True,
1170
    repr=True,
1171
    eq=True,
1172
    order=False,
1173
    unsafe_hash=False,
1174
    frozen=False,
1175
):
1176
    """Return a new dynamically created dataclass.
1177
1178
    The dataclass name will be 'cls_name'.  'fields' is an iterable
1179
    of either (name), (name, type) or (name, type, Field) objects. If type is
1180
    omitted, use the string 'typing.Any'.  Field objects are created by
1181
    the equivalent of calling 'field(name, type [, Field-info])'.
1182
1183
      C = make_dataclass('C', ['x', ('y', int), ('z', int, field(init=False))], bases=(Base,))
1184
1185
    is equivalent to:
1186
1187
      @dataclass
1188
      class C(Base):
1189
          x: 'typing.Any'
1190
          y: int
1191
          z: int = field(init=False)
1192
1193
    For the bases and namespace parameters, see the builtin type() function.
1194
1195
    The parameters init, repr, eq, order, unsafe_hash, and frozen are passed to
1196
    dataclass().
1197
    """
1198
1199
    if namespace is None:
1200
        namespace = {}
1201
    else:
1202
        # Copy namespace since we're going to mutate it.
1203
        namespace = namespace.copy()
1204
1205
    # While we're looking through the field names, validate that they
1206
    # are identifiers, are not keywords, and not duplicates.
1207
    seen = set()
1208
    anns = {}
1209
    for item in fields:
1210
        if isinstance(item, str):
1211
            name = item
1212
            tp = "typing.Any"
1213
        elif len(item) == 2:
1214
            (
1215
                name,
1216
                tp,
1217
            ) = item
1218
        elif len(item) == 3:
1219
            name, tp, spec = item
1220
            namespace[name] = spec
1221
        else:
1222
            raise TypeError(f"Invalid field: {item!r}")
1223
1224
        if not isinstance(name, str) or not name.isidentifier():
1225
            raise TypeError(f"Field names must be valid identifiers: {name!r}")
1226
        if keyword.iskeyword(name):
1227
            raise TypeError(f"Field names must not be keywords: {name!r}")
1228
        if name in seen:
1229
            raise TypeError(f"Field name duplicated: {name!r}")
1230
1231
        seen.add(name)
1232
        anns[name] = tp
1233
1234
    namespace["__annotations__"] = anns
1235
    # We use `types.new_class()` instead of simply `type()` to allow dynamic creation
1236
    # of generic dataclassses.
1237
    cls = types.new_class(cls_name, bases, {}, lambda ns: ns.update(namespace))
1238
    return dataclass(
1239
        cls,
1240
        init=init,
1241
        repr=repr,
1242
        eq=eq,
1243
        order=order,
1244
        unsafe_hash=unsafe_hash,
1245
        frozen=frozen,
1246
    )
1247
1248
1249
def replace(obj, **changes):
1250
    """Return a new object replacing specified fields with new values.
1251
1252
    This is especially useful for frozen classes.  Example usage:
1253
1254
      @dataclass(frozen=True)
1255
      class C:
1256
          x: int
1257
          y: int
1258
1259
      c = C(1, 2)
1260
      c1 = replace(c, x=3)
1261
      assert c1.x == 3 and c1.y == 2
1262
    """
1263
1264
    # We're going to mutate 'changes', but that's okay because it's a
1265
    # new dict, even if called with 'replace(obj, **my_changes)'.
1266
1267
    if not _is_dataclass_instance(obj):
1268
        raise TypeError("replace() should be called on dataclass instances")
1269
1270
    # It's an error to have init=False fields in 'changes'.
1271
    # If a field is not in 'changes', read its value from the provided obj.
1272
    for f in getattr(obj, _FIELDS).values():
1273
        # Only consider normal fields or InitVars.
1274
        # pylint: disable=protected-access
1275
        if f._field_type is _FIELD_CLASSVAR:
1276
            continue
1277
1278
        if not f.init:
1279
            # Error if this field is specified in changes.
1280
            if f.name in changes:
1281
                raise ValueError(
1282
                    f"field {f.name} is declared with "
1283
                    "init=False, it cannot be specified with "
1284
                    "replace()"
1285
                )
1286
            continue
1287
1288
        if f.name not in changes:
1289
            # pylint: disable=protected-access
1290
            if f._field_type is _FIELD_INITVAR:
1291
                raise ValueError(
1292
                    f"InitVar {f.name!r} " "must be specified with replace()"
1293
                )
1294
            changes[f.name] = getattr(obj, f.name)
1295
1296
    # Create the new object, which calls __init__() and
1297
    # __post_init__() (if defined), using all of the init fields we've
1298
    # added and/or left in 'changes'.  If there are values supplied in
1299
    # changes that aren't fields, this will correctly raise a
1300
    # TypeError.
1301
    return obj.__class__(**changes)
1302