dataclass-wizard 0.36.3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. dataclass_wizard/__init__.py +148 -0
  2. dataclass_wizard/__version__.py +14 -0
  3. dataclass_wizard/abstractions.py +680 -0
  4. dataclass_wizard/abstractions.pyi +850 -0
  5. dataclass_wizard/bases.py +582 -0
  6. dataclass_wizard/bases_meta.py +401 -0
  7. dataclass_wizard/bases_meta.pyi +109 -0
  8. dataclass_wizard/class_helper.py +609 -0
  9. dataclass_wizard/class_helper.pyi +319 -0
  10. dataclass_wizard/constants.py +57 -0
  11. dataclass_wizard/decorators.py +252 -0
  12. dataclass_wizard/dumpers.py +517 -0
  13. dataclass_wizard/enums.py +52 -0
  14. dataclass_wizard/environ/lookups.pyi +60 -0
  15. dataclass_wizard/environ/wizard.pyi +72 -0
  16. dataclass_wizard/errors.py +518 -0
  17. dataclass_wizard/errors.pyi +265 -0
  18. dataclass_wizard/lazy_imports.py +29 -0
  19. dataclass_wizard/loader_selection.py +221 -0
  20. dataclass_wizard/loaders.py +785 -0
  21. dataclass_wizard/log.py +7 -0
  22. dataclass_wizard/models.py +550 -0
  23. dataclass_wizard/models.pyi +545 -0
  24. dataclass_wizard/parsers.py +628 -0
  25. dataclass_wizard/property_wizard.py +354 -0
  26. dataclass_wizard/py.typed +1 -0
  27. dataclass_wizard/serial_json.py +194 -0
  28. dataclass_wizard/serial_json.pyi +202 -0
  29. dataclass_wizard/type_def.py +233 -0
  30. dataclass_wizard/utils/object_path.pyi +86 -0
  31. dataclass_wizard/v1/models.pyi +654 -0
  32. dataclass_wizard/wizard_mixins.py +303 -0
  33. dataclass_wizard/wizard_mixins.pyi +128 -0
  34. dataclass_wizard-0.36.3.dist-info/METADATA +1756 -0
  35. dataclass_wizard-0.36.3.dist-info/RECORD +39 -0
  36. dataclass_wizard-0.36.3.dist-info/WHEEL +5 -0
  37. dataclass_wizard-0.36.3.dist-info/entry_points.txt +2 -0
  38. dataclass_wizard-0.36.3.dist-info/licenses/LICENSE +16 -0
  39. dataclass_wizard-0.36.3.dist-info/top_level.txt +1 -0
@@ -0,0 +1,680 @@
1
+ """
2
+ Contains implementations for Abstract Base Classes
3
+ """
4
+ import json
5
+
6
+ from abc import ABC, abstractmethod
7
+ from dataclasses import dataclass, InitVar, Field
8
+ from typing import Type, TypeVar, Dict, Generic
9
+
10
+ from .bases import META
11
+ from .models import Extras
12
+ from .v1.models import Extras as V1Extras, TypeInfo
13
+ from .type_def import T, TT
14
+
15
+
16
+ # Create a generic variable that can be 'AbstractJSONWizard', or any subclass.
17
+ W = TypeVar('W', bound='AbstractJSONWizard')
18
+
19
+
20
+ class AbstractEnvWizard(ABC):
21
+ """
22
+ Abstract class that defines the methods a sub-class must implement at a
23
+ minimum to be considered a "true" Environment Wizard.
24
+ """
25
+ __slots__ = ()
26
+
27
+ # Extends the `__annotations__` attribute to return only the fields
28
+ # (variables) of the `EnvWizard` subclass.
29
+ #
30
+ # .. NOTE::
31
+ # This excludes fields marked as ``ClassVar``, or ones which are
32
+ # not type-annotated.
33
+ __fields__: dict[str, Field]
34
+
35
+ def dict(self):
36
+ ...
37
+
38
+ @abstractmethod
39
+ def to_dict(self):
40
+ ...
41
+
42
+ @abstractmethod
43
+ def to_json(self, indent=None):
44
+ ...
45
+
46
+
47
+ class AbstractJSONWizard(ABC):
48
+
49
+ __slots__ = ()
50
+
51
+ @classmethod
52
+ @abstractmethod
53
+ def from_json(cls, string):
54
+ ...
55
+
56
+ @classmethod
57
+ @abstractmethod
58
+ def from_list(cls, o):
59
+ ...
60
+
61
+ @classmethod
62
+ @abstractmethod
63
+ def from_dict(cls, o):
64
+ ...
65
+
66
+ @abstractmethod
67
+ def to_dict(self):
68
+ ...
69
+
70
+ @abstractmethod
71
+ def to_json(self, *,
72
+ encoder=json.dumps,
73
+ indent=None,
74
+ **encoder_kwargs):
75
+ ...
76
+
77
+ @classmethod
78
+ @abstractmethod
79
+ def list_to_json(cls,
80
+ instances,
81
+ encoder=json.dumps,
82
+ indent=None,
83
+ **encoder_kwargs):
84
+ ...
85
+
86
+
87
+ @dataclass
88
+ class AbstractParser(ABC, Generic[T, TT]):
89
+
90
+ __slots__ = ('base_type', )
91
+
92
+ # Please see `abstractions.pyi` for documentation on each field.
93
+
94
+ cls: InitVar[Type]
95
+ extras: InitVar[Extras]
96
+ base_type: type[T]
97
+
98
+ def __contains__(self, item):
99
+ return type(item) is self.base_type
100
+
101
+ @abstractmethod
102
+ def __call__(self, o) -> TT:
103
+ ...
104
+
105
+
106
+ class AbstractLoader(ABC):
107
+
108
+ __slots__ = ()
109
+
110
+ @staticmethod
111
+ @abstractmethod
112
+ def transform_json_field(string):
113
+ ...
114
+
115
+ @staticmethod
116
+ @abstractmethod
117
+ def default_load_to(o, _):
118
+ ...
119
+
120
+ @staticmethod
121
+ @abstractmethod
122
+ def load_after_type_check(o, base_type):
123
+ ...
124
+
125
+ @staticmethod
126
+ @abstractmethod
127
+ def load_to_str(o, base_type):
128
+ ...
129
+
130
+ @staticmethod
131
+ @abstractmethod
132
+ def load_to_int(o, base_type):
133
+ ...
134
+
135
+ @staticmethod
136
+ @abstractmethod
137
+ def load_to_float(o, base_type):
138
+ ...
139
+
140
+ @staticmethod
141
+ @abstractmethod
142
+ def load_to_bool(o, _):
143
+ ...
144
+
145
+ @staticmethod
146
+ @abstractmethod
147
+ def load_to_enum(o, base_type):
148
+ ...
149
+
150
+ @staticmethod
151
+ @abstractmethod
152
+ def load_to_uuid(o, base_type):
153
+ ...
154
+
155
+ @staticmethod
156
+ @abstractmethod
157
+ def load_to_iterable(
158
+ o, base_type,
159
+ elem_parser):
160
+ ...
161
+
162
+ @staticmethod
163
+ @abstractmethod
164
+ def load_to_tuple(
165
+ o, base_type,
166
+ elem_parsers):
167
+ ...
168
+
169
+ @staticmethod
170
+ @abstractmethod
171
+ def load_to_named_tuple(
172
+ o, base_type,
173
+ field_to_parser,
174
+ field_parsers):
175
+ ...
176
+
177
+ @staticmethod
178
+ @abstractmethod
179
+ def load_to_named_tuple_untyped(
180
+ o, base_type,
181
+ dict_parser, list_parser):
182
+ ...
183
+
184
+ @staticmethod
185
+ @abstractmethod
186
+ def load_to_dict(
187
+ o, base_type,
188
+ key_parser,
189
+ val_parser):
190
+ ...
191
+
192
+ @staticmethod
193
+ @abstractmethod
194
+ def load_to_defaultdict(
195
+ o, base_type,
196
+ default_factory,
197
+ key_parser,
198
+ val_parser):
199
+ ...
200
+
201
+ @staticmethod
202
+ @abstractmethod
203
+ def load_to_typed_dict(
204
+ o, base_type,
205
+ key_to_parser,
206
+ required_keys,
207
+ optional_keys):
208
+ ...
209
+
210
+ @staticmethod
211
+ @abstractmethod
212
+ def load_to_decimal(o, base_type):
213
+ ...
214
+
215
+ @staticmethod
216
+ @abstractmethod
217
+ def load_to_datetime(o, base_type):
218
+ ...
219
+
220
+ @staticmethod
221
+ @abstractmethod
222
+ def load_to_time(o, base_type):
223
+ ...
224
+
225
+ @staticmethod
226
+ @abstractmethod
227
+ def load_to_date(o, base_type):
228
+ ...
229
+
230
+ @staticmethod
231
+ @abstractmethod
232
+ def load_to_timedelta(o, base_type):
233
+ ...
234
+
235
+ # @staticmethod
236
+ # @abstractmethod
237
+ # def load_func_for_dataclass(
238
+ # cls: Type[T],
239
+ # config: Optional[META],
240
+ # ) -> Callable[[JSONObject], T]:
241
+ # """
242
+ # Generate and return the load function for a (nested) dataclass of
243
+ # type `cls`.
244
+ # """
245
+
246
+ @classmethod
247
+ @abstractmethod
248
+ def get_parser_for_annotation(cls, ann_type,
249
+ base_cls=None,
250
+ extras=None):
251
+ ...
252
+
253
+
254
+ class AbstractDumper(ABC):
255
+ __slots__ = ()
256
+
257
+
258
+ class AbstractLoaderGenerator(ABC):
259
+ """
260
+ Abstract code generator which defines helper methods to generate the
261
+ code for deserializing an object `o` of a given annotated type into
262
+ the corresponding dataclass field during dynamic function construction.
263
+ """
264
+ __slots__ = ()
265
+
266
+ @staticmethod
267
+ @abstractmethod
268
+ def transform_json_field(string: str) -> str:
269
+ """
270
+ Transform a JSON field name (which will typically be camel-cased)
271
+ into the conventional format for a dataclass field name
272
+ (which will ideally be snake-cased).
273
+ """
274
+
275
+ @staticmethod
276
+ @abstractmethod
277
+ def default_load_to(tp: TypeInfo, extras: V1Extras) -> str:
278
+ """
279
+ Generate code for the default load function if no other types match.
280
+ Generally, this will be a stub load method.
281
+ """
282
+
283
+ @staticmethod
284
+ @abstractmethod
285
+ def load_to_str(tp: TypeInfo, extras: V1Extras) -> str:
286
+ """
287
+ Generate code to load a value into a string field.
288
+ """
289
+
290
+ @staticmethod
291
+ @abstractmethod
292
+ def load_to_int(tp: TypeInfo, extras: V1Extras) -> str:
293
+ """
294
+ Generate code to load a value into an integer field.
295
+ """
296
+
297
+ @staticmethod
298
+ @abstractmethod
299
+ def load_to_float(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
300
+ """
301
+ Generate code to load a value into a float field.
302
+ """
303
+
304
+ @staticmethod
305
+ @abstractmethod
306
+ def load_to_bool(_: str, extras: V1Extras) -> str:
307
+ """
308
+ Generate code to load a value into a boolean field.
309
+ Adds a helper function `as_bool` to the local context.
310
+ """
311
+
312
+ @staticmethod
313
+ @abstractmethod
314
+ def load_to_bytes(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
315
+ """
316
+ Generate code to load a value into a bytes field.
317
+ """
318
+
319
+ @staticmethod
320
+ @abstractmethod
321
+ def load_to_bytearray(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
322
+ """
323
+ Generate code to load a value into a bytearray field.
324
+ """
325
+
326
+ @staticmethod
327
+ @abstractmethod
328
+ def load_to_none(tp: TypeInfo, extras: V1Extras) -> str:
329
+ """
330
+ Generate code to load a value into a None.
331
+ """
332
+
333
+ @staticmethod
334
+ @abstractmethod
335
+ def load_to_literal(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
336
+ """
337
+ Generate code to confirm a value is equivalent to one
338
+ of the provided literals.
339
+ """
340
+
341
+ @classmethod
342
+ @abstractmethod
343
+ def load_to_union(cls, tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
344
+ """
345
+ Generate code to load a value into a `Union[X, Y, ...]` (one of [X, Y, ...] possible types)
346
+ """
347
+
348
+ @staticmethod
349
+ @abstractmethod
350
+ def load_to_enum(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
351
+ """
352
+ Generate code to load a value into an Enum field.
353
+ """
354
+
355
+ @staticmethod
356
+ @abstractmethod
357
+ def load_to_uuid(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
358
+ """
359
+ Generate code to load a value into a UUID field.
360
+ """
361
+
362
+ @staticmethod
363
+ @abstractmethod
364
+ def load_to_iterable(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
365
+ """
366
+ Generate code to load a value into an iterable field (list, set, etc.).
367
+ """
368
+
369
+ @staticmethod
370
+ @abstractmethod
371
+ def load_to_tuple(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
372
+ """
373
+ Generate code to load a value into a tuple field.
374
+ """
375
+
376
+ @staticmethod
377
+ @abstractmethod
378
+ def load_to_named_tuple(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
379
+ """
380
+ Generate code to load a value into a named tuple field.
381
+ """
382
+
383
+ @classmethod
384
+ @abstractmethod
385
+ def load_to_named_tuple_untyped(cls, tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
386
+ """
387
+ Generate code to load a value into an untyped named tuple.
388
+ """
389
+
390
+ @staticmethod
391
+ @abstractmethod
392
+ def load_to_dict(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
393
+ """
394
+ Generate code to load a value into a dictionary field.
395
+ """
396
+
397
+ @staticmethod
398
+ @abstractmethod
399
+ def load_to_defaultdict(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
400
+ """
401
+ Generate code to load a value into a defaultdict field.
402
+ """
403
+
404
+ @staticmethod
405
+ @abstractmethod
406
+ def load_to_typed_dict(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
407
+ """
408
+ Generate code to load a value into a typed dictionary field.
409
+ """
410
+
411
+ @staticmethod
412
+ @abstractmethod
413
+ def load_to_decimal(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
414
+ """
415
+ Generate code to load a value into a Decimal field.
416
+ """
417
+
418
+ @staticmethod
419
+ @abstractmethod
420
+ def load_to_path(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
421
+ """
422
+ Generate code to load a value into a Decimal field.
423
+ """
424
+
425
+ @staticmethod
426
+ @abstractmethod
427
+ def load_to_datetime(tp: TypeInfo, extras: V1Extras) -> str:
428
+ """
429
+ Generate code to load a value into a datetime field.
430
+ """
431
+
432
+ @staticmethod
433
+ @abstractmethod
434
+ def load_to_time(tp: TypeInfo, extras: V1Extras) -> str:
435
+ """
436
+ Generate code to load a value into a time field.
437
+ """
438
+
439
+ @staticmethod
440
+ @abstractmethod
441
+ def load_to_date(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
442
+ """
443
+ Generate code to load a value into a date field.
444
+ """
445
+
446
+ @staticmethod
447
+ @abstractmethod
448
+ def load_to_timedelta(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
449
+ """
450
+ Generate code to load a value into a timedelta field.
451
+ """
452
+
453
+ @staticmethod
454
+ def load_to_dataclass(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
455
+ """
456
+ Generate code to load a value into a `dataclass` type field.
457
+ """
458
+
459
+ @classmethod
460
+ @abstractmethod
461
+ def get_string_for_annotation(cls,
462
+ tp: TypeInfo,
463
+ extras: V1Extras) -> 'str | TypeInfo':
464
+ """
465
+ Generate code to get the parser (dispatcher) for a given annotation type.
466
+
467
+ `base_cls` is the original class object, useful when the annotated
468
+ type is a :class:`typing.ForwardRef` object.
469
+ """
470
+
471
+
472
+ class AbstractDumperGenerator(ABC):
473
+ """
474
+ Abstract code generator which defines helper methods to generate the
475
+ code for deserializing an object `o` of a given annotated type into
476
+ the corresponding dataclass field during dynamic function construction.
477
+ """
478
+ __slots__ = ()
479
+
480
+ @staticmethod
481
+ @abstractmethod
482
+ def transform_dataclass_field(string: str) -> str:
483
+ """
484
+ Transform a dataclass field name (which will ideally be snake-cased)
485
+ into the conventional format for a JSON field name.
486
+ """
487
+
488
+ @staticmethod
489
+ @abstractmethod
490
+ def default_dump_from(tp: TypeInfo, extras: V1Extras) -> str:
491
+ """
492
+ Generate code for the default dump function if no other types match.
493
+ Generally, this will be a stub dump method.
494
+ """
495
+
496
+ @staticmethod
497
+ @abstractmethod
498
+ def dump_from_str(tp: TypeInfo, extras: V1Extras) -> str:
499
+ """
500
+ Generate code to dump a value from a string field.
501
+ """
502
+
503
+ @staticmethod
504
+ @abstractmethod
505
+ def dump_from_int(tp: TypeInfo, extras: V1Extras) -> str:
506
+ """
507
+ Generate code to dump a value from an integer field.
508
+ """
509
+
510
+ @staticmethod
511
+ @abstractmethod
512
+ def dump_from_float(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
513
+ """
514
+ Generate code to dump a value from a float field.
515
+ """
516
+
517
+ @staticmethod
518
+ @abstractmethod
519
+ def dump_from_bool(_: str, extras: V1Extras) -> str:
520
+ """
521
+ Generate code to dump a value from a boolean field.
522
+ """
523
+
524
+ @staticmethod
525
+ @abstractmethod
526
+ def dump_from_bytes(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
527
+ """
528
+ Generate code to dump a value from a bytes field.
529
+ """
530
+
531
+ @staticmethod
532
+ @abstractmethod
533
+ def dump_from_bytearray(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
534
+ """
535
+ Generate code to dump a value from a bytearray field.
536
+ """
537
+
538
+ @staticmethod
539
+ @abstractmethod
540
+ def dump_from_none(tp: TypeInfo, extras: V1Extras) -> str:
541
+ """
542
+ Generate code to dump a value from a None.
543
+ """
544
+
545
+ @staticmethod
546
+ @abstractmethod
547
+ def dump_from_literal(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
548
+ """
549
+ Generate code to dump a literal.
550
+ """
551
+
552
+ @classmethod
553
+ @abstractmethod
554
+ def dump_from_union(cls, tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
555
+ """
556
+ Generate code to dump a value from a `Union[X, Y, ...]` (one of [X, Y, ...] possible types)
557
+ """
558
+
559
+ @staticmethod
560
+ @abstractmethod
561
+ def dump_from_enum(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
562
+ """
563
+ Generate code to dump a value from an Enum field.
564
+ """
565
+
566
+ @staticmethod
567
+ @abstractmethod
568
+ def dump_from_uuid(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
569
+ """
570
+ Generate code to dump a value from a UUID field.
571
+ """
572
+
573
+ @staticmethod
574
+ @abstractmethod
575
+ def dump_from_iterable(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
576
+ """
577
+ Generate code to dump a value from an iterable field (list, set, etc.).
578
+ """
579
+
580
+ @staticmethod
581
+ @abstractmethod
582
+ def dump_from_tuple(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
583
+ """
584
+ Generate code to dump a value from a tuple field.
585
+ """
586
+
587
+ @staticmethod
588
+ @abstractmethod
589
+ def dump_from_named_tuple(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
590
+ """
591
+ Generate code to dump a value from a named tuple field.
592
+ """
593
+
594
+ @classmethod
595
+ @abstractmethod
596
+ def dump_from_named_tuple_untyped(cls, tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
597
+ """
598
+ Generate code to dump a value from an untyped named tuple.
599
+ """
600
+
601
+ @staticmethod
602
+ @abstractmethod
603
+ def dump_from_dict(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
604
+ """
605
+ Generate code to dump a value from a dictionary field.
606
+ """
607
+
608
+ @staticmethod
609
+ @abstractmethod
610
+ def dump_from_defaultdict(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
611
+ """
612
+ Generate code to dump a value from a defaultdict field.
613
+ """
614
+
615
+ @staticmethod
616
+ @abstractmethod
617
+ def dump_from_typed_dict(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
618
+ """
619
+ Generate code to dump a value from a typed dictionary field.
620
+ """
621
+
622
+ @staticmethod
623
+ @abstractmethod
624
+ def dump_from_decimal(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
625
+ """
626
+ Generate code to dump a value from a Decimal field.
627
+ """
628
+
629
+ @staticmethod
630
+ @abstractmethod
631
+ def dump_from_path(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
632
+ """
633
+ Generate code to dump a value from a Decimal field.
634
+ """
635
+
636
+ @staticmethod
637
+ @abstractmethod
638
+ def dump_from_datetime(tp: TypeInfo, extras: V1Extras) -> str:
639
+ """
640
+ Generate code to dump a value from a datetime field.
641
+ """
642
+
643
+ @staticmethod
644
+ @abstractmethod
645
+ def dump_from_time(tp: TypeInfo, extras: V1Extras) -> str:
646
+ """
647
+ Generate code to dump a value from a time field.
648
+ """
649
+
650
+ @staticmethod
651
+ @abstractmethod
652
+ def dump_from_date(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
653
+ """
654
+ Generate code to dump a value from a date field.
655
+ """
656
+
657
+ @staticmethod
658
+ @abstractmethod
659
+ def dump_from_timedelta(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
660
+ """
661
+ Generate code to dump a value from a timedelta field.
662
+ """
663
+
664
+ @staticmethod
665
+ def dump_from_dataclass(tp: TypeInfo, extras: V1Extras) -> 'str | TypeInfo':
666
+ """
667
+ Generate code to dump a value from a `dataclass` type field.
668
+ """
669
+
670
+ @classmethod
671
+ @abstractmethod
672
+ def get_string_for_annotation(cls,
673
+ tp: TypeInfo,
674
+ extras: V1Extras) -> 'str | TypeInfo':
675
+ """
676
+ Generate code to get the parser (dispatcher) for a given annotation type.
677
+
678
+ `base_cls` is the original class object, useful when the annotated
679
+ type is a :class:`typing.ForwardRef` object.
680
+ """