From 989416b1baa82e99fd387470828c0d5f56abf3bc Mon Sep 17 00:00:00 2001 From: Ritvik Nag Date: Sun, 8 Dec 2024 11:49:26 -0500 Subject: [PATCH] Moar Updates * Add support for `Sequence`, `Collection`, and `MutableSequence` * Add support for container class with `Meta` config (#163) * Fix for `NoneType` --- dataclass_wizard/class_helper.py | 14 ++++++++++++++ dataclass_wizard/type_def.py | 11 +++++++++-- dataclass_wizard/v1/loaders.py | 14 ++++++++++++-- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/dataclass_wizard/class_helper.py b/dataclass_wizard/class_helper.py index d6dc242..a0af386 100644 --- a/dataclass_wizard/class_helper.py +++ b/dataclass_wizard/class_helper.py @@ -303,10 +303,24 @@ def call_meta_initializer_if_needed(cls): """ Calls the Meta initializer when the inner :class:`Meta` is sub-classed. """ + # TODO add tests + + cls_module = cls.__module__ + + # skip classes provided by this library + if cls_module.startswith('dataclass_wizard.'): + return + cls_name = get_class_name(cls) if cls_name in META_INITIALIZER: META_INITIALIZER[cls_name](cls) + else: + for base in cls.__bases__: + base_cls_name = get_class_name(base) + + if base_cls_name in META_INITIALIZER: + META_INITIALIZER[base_cls_name](cls) def get_meta(cls, base_cls=AbstractMeta): diff --git a/dataclass_wizard/type_def.py b/dataclass_wizard/type_def.py index 5d5bd68..dbbb45a 100644 --- a/dataclass_wizard/type_def.py +++ b/dataclass_wizard/type_def.py @@ -45,7 +45,6 @@ from datetime import date, time, datetime from enum import Enum from os import PathLike -from types import NoneType from typing import ( Any, Type, TypeVar, Sequence, Mapping, List, Dict, DefaultDict, FrozenSet, Union, NamedTuple, Callable, AnyStr, TextIO, BinaryIO, @@ -56,7 +55,15 @@ ) from uuid import UUID -from .constants import PY311_OR_ABOVE, PY313_OR_ABOVE +from .constants import PY310_OR_ABOVE, PY311_OR_ABOVE, PY313_OR_ABOVE + + +# The class of the `None` singleton, cached for re-usability +if PY310_OR_ABOVE: + # https://docs.python.org/3/library/types.html#types.NoneType + from types import NoneType +else: + NoneType = type(None) # Type check for numeric types - needed because `bool` is technically # a Number. diff --git a/dataclass_wizard/v1/loaders.py b/dataclass_wizard/v1/loaders.py index 15e7447..9d14c2c 100644 --- a/dataclass_wizard/v1/loaders.py +++ b/dataclass_wizard/v1/loaders.py @@ -1,6 +1,5 @@ # TODO cleanup imports -import types from base64 import decodebytes from collections import defaultdict, deque, namedtuple import collections.abc as abc @@ -37,6 +36,7 @@ from ..models import Extras, PatternedDT, TypeInfo from ..parsers import * from ..type_def import ( + NoneType, ExplicitNull, FrozenKeys, DefFactory, NoneType, JSONObject, PyRequired, PyNotRequired, PyLiteralString, M, N, T, E, U, DD, LSQ, NT @@ -60,7 +60,7 @@ # returns the same object. We can provide a fast-path for these types in asdict and astuple. _SIMPLE_TYPES = ( # Common JSON Serializable types - types.NoneType, + NoneType, bool, int, float, @@ -718,6 +718,16 @@ def get_string_for_annotation(cls, # for the `cls` (base_type) load_hook = cls.load_to_dataclass + elif origin in (abc.Sequence, abc.MutableSequence, abc.Collection): + load_hook = cls.load_to_iterable + # desired (non-generic) origin type + origin = tuple if origin is abc.Sequence else list + # Get type arguments, e.g. `Sequence[int]` -> `int` + try: + args = get_args(type_ann) + except ValueError: + args = Any, + else: # TODO everything should use `get_origin_v2`