dataclass: replace print spam with decent logging

This commit is contained in:
InsanePrawn 2023-04-16 03:28:33 +02:00
parent 28a5400d48
commit 572142bf0b

View file

@ -1,5 +1,6 @@
from __future__ import annotations from __future__ import annotations
import logging
import toml import toml
from dataclasses import dataclass from dataclasses import dataclass
@ -41,7 +42,6 @@ def resolve_dict_hints(hints: Any) -> Generator[tuple[Any, ...], None, None]:
t_origin = get_origin(hint) t_origin = get_origin(hint)
t_args = get_args(hint) t_args = get_args(hint)
if t_origin == dict: if t_origin == dict:
print(f"Yielding {t_args=}")
yield t_args yield t_args
continue continue
if t_origin in [NoneType, Optional, Union, UnionType] and t_args: if t_origin in [NoneType, Optional, Union, UnionType] and t_args:
@ -66,15 +66,17 @@ class DataClass(Munch):
allow_extra: bool = False, allow_extra: bool = False,
type_hints: Optional[dict[str, Any]] = None, type_hints: Optional[dict[str, Any]] = None,
) -> Any: ) -> Any:
results = {} results: dict[str, Any] = {}
values = dict(values) values = dict(values)
print(f"\ntransform function:\n{values}, {type_hints=}")
for key in list(values.keys()): for key in list(values.keys()):
value = values.pop(key) value = values.pop(key)
type_hints = cls._type_hints if type_hints is None else type_hints type_hints = cls._type_hints if type_hints is None else type_hints
if key in type_hints: if key in type_hints:
_classes = tuple[type](resolve_type_hint(type_hints[key])) _classes = tuple[type](resolve_type_hint(type_hints[key]))
optional = NoneType in _classes optional = NoneType in _classes
if optional and value is None:
results[key] = None
continue
if issubclass(_classes[0], dict): if issubclass(_classes[0], dict):
assert isinstance(value, dict) or optional assert isinstance(value, dict) or optional
target_class = _classes[0] target_class = _classes[0]
@ -85,34 +87,37 @@ class DataClass(Munch):
break break
if target_class is dict: if target_class is dict:
dict_hints = list(resolve_dict_hints(type_hints[key])) dict_hints = list(resolve_dict_hints(type_hints[key]))
print(f"Got {key=} {dict_hints=}")
if len(dict_hints) != 1: if len(dict_hints) != 1:
print(f"Received wrong amount of type hints for key {key}: {len(dict_hints)}") msg = f"transform(): Received wrong amount of type hints for key {key}: {len(dict_hints)}"
if validate:
raise Exception(msg)
logging.warning(msg)
if len(dict_hints) == 1 and value is not None: if len(dict_hints) == 1 and value is not None:
if len(dict_hints[0]) != 2 or not all(dict_hints[0]): if len(dict_hints[0]) != 2 or not all(dict_hints[0]):
print(f"Weird dict hints received: {dict_hints}") logging.debug(f"Weird dict hints received: {dict_hints}")
continue continue
key_type, value_type = dict_hints[0] key_type, value_type = dict_hints[0]
if not isinstance(value, Mapping): if not isinstance(value, Mapping):
msg = f"Got non-mapping {value!r} for expected dict type: {key_type} => {value_type}. Allowed classes: {_classes}"
if validate: if validate:
raise Exception( raise Exception(msg)
f"Got non-mapping {value!r} for expected dict type: {key_type} => {value_type}. Allowed classes: {_classes}") logging.warning(msg)
print(f"Got non-mapping {value!r} for expected dict type: {key_type} => {value_type}. Allowed classes: {_classes}")
results[key] = value results[key] = value
continue continue
if isinstance(key_type, type): if isinstance(key_type, type):
if issubclass(key_type, str): if issubclass(key_type, str):
target_class = Munch target_class = Munch
else: else:
print(f"{key=} DICT WRONG KEY TYPE: {key_type}") msg = f"{key=} subdict got wrong key type hint (expected str): {key_type}"
if validate:
raise Exception(msg)
logging.warning(msg)
if validate: if validate:
for k in value: for k in value:
if not isinstance(k, tuple(flatten_hints(key_type))): if not isinstance(k, tuple(flatten_hints(key_type))):
raise Exception(f'Subdict "{key}": wrong type for subkey "{k}": got: {type(k)}, expected: {key_type}') raise Exception(f'Subdict "{key}": wrong type for subkey "{k}": got: {type(k)}, expected: {key_type}')
dict_content_hints = {k: value_type for k in value} dict_content_hints = {k: value_type for k in value}
print(f"tranforming: {value=} {dict_content_hints=}")
value = cls.transform(value, validate=validate, allow_extra=allow_extra, type_hints=dict_content_hints) value = cls.transform(value, validate=validate, allow_extra=allow_extra, type_hints=dict_content_hints)
print(f"tranformed: {value=}")
if not isinstance(value, target_class): if not isinstance(value, target_class):
if not (optional and value is None): if not (optional and value is None):
assert issubclass(target_class, Munch) assert issubclass(target_class, Munch)
@ -120,7 +125,8 @@ class DataClass(Munch):
kwargs = {'validate': validate} if issubclass(target_class, DataClass) else {} kwargs = {'validate': validate} if issubclass(target_class, DataClass) else {}
value = target_class(value, **kwargs) # type:ignore[attr-defined] value = target_class(value, **kwargs) # type:ignore[attr-defined]
else: else:
print(f"nothing to do: '{key}' was already {target_class}") # print(f"nothing to do: '{key}' was already {target_class})
pass
# handle numerics # handle numerics
elif set(_classes).intersection([int, float]) and isinstance(value, str) and str not in _classes: elif set(_classes).intersection([int, float]) and isinstance(value, str) and str not in _classes:
parsed_number = None parsed_number = None
@ -145,7 +151,6 @@ class DataClass(Munch):
f'{" ,".join([ c.__name__ for c in _classes])}; ' f'{" ,".join([ c.__name__ for c in _classes])}; '
f'got: {type(value).__name__}; value: {value}') f'got: {type(value).__name__}; value: {value}')
elif validate and not allow_extra: elif validate and not allow_extra:
import logging
logging.debug(f"{cls}: unknown key '{key}': {value}") logging.debug(f"{cls}: unknown key '{key}': {value}")
raise Exception(f'{cls}: Unknown key "{key}"') raise Exception(f'{cls}: Unknown key "{key}"')
else: else:
@ -183,6 +188,7 @@ class DataClass(Munch):
sparse: Optional[bool] = None, sparse: Optional[bool] = None,
recursive: bool = True, recursive: bool = True,
hints: Optional[dict[str, Any]] = None, hints: Optional[dict[str, Any]] = None,
validate: bool = True,
) -> dict[Any, Any]: ) -> dict[Any, Any]:
# preserve original None-type args # preserve original None-type args
_sparse = cls._sparse if sparse is None else sparse _sparse = cls._sparse if sparse is None else sparse
@ -190,64 +196,55 @@ class DataClass(Munch):
hints = cls._type_hints if hints is None else hints hints = cls._type_hints if hints is None else hints
result = dict(d) result = dict(d)
if not (_strip_hidden or _sparse or result): if not (_strip_hidden or _sparse or result):
print(f"shortcircuiting {d=}")
return result return result
print(f"Stripping {result} with hints: {hints}")
for k, v in d.items(): for k, v in d.items():
type_hint = resolve_type_hint(hints.get(k, "abc")) type_hint = resolve_type_hint(hints.get(k, "abc"))
print(f"Working on key {k}, type hints: {type_hint}")
if not isinstance(k, str): if not isinstance(k, str):
print(f"skipping unknown key type {k=}") msg = f"strip_dict(): unknown key type {k=}: {type(k)=}"
if validate:
raise Exception(msg)
logging.warning(f"{msg} (skipping)")
continue continue
if strip_hidden and k.startswith('_'): if _strip_hidden and k.startswith('_'):
result.pop(k) result.pop(k)
continue continue
if v is None: if v is None:
if NoneType not in type_hint: if NoneType not in type_hint:
msg = f'encountered illegal null value at key "{k}" for typehint {type_hint}' msg = f'encountered illegal null value at key "{k}" for typehint {type_hint}'
if True: if validate:
raise Exception(msg) raise Exception(msg)
print(msg) logging.warning(msg)
if _sparse: if _sparse:
print(f"popping empty {k}")
result.pop(k) result.pop(k)
continue continue
print(f"encountered legal null value at {k}: {_sparse=}")
if recursive and isinstance(v, dict): if recursive and isinstance(v, dict):
if not v: if not v:
result[k] = {} result[k] = {}
continue continue
if isinstance(v, DataClass): if isinstance(v, DataClass):
print(f"Dataclass detected in {k=}") # pass None in sparse and strip_hidden
result[k] = v.toDict(strip_hidden=strip_hidden, sparse=sparse) # pass None in sparse and strip_hidden result[k] = v.toDict(strip_hidden=strip_hidden, sparse=sparse)
continue continue
if isinstance(v, Munch): if isinstance(v, Munch):
print(f"Converting munch {k=}")
result[k] = v.toDict() result[k] = v.toDict()
if k not in hints: if k not in hints:
print(f"skipping unknown {k=}")
continue continue
print(f"STRIPPING RECURSIVELY: {k}: {v}, parent hints: {hints[k]}")
_subhints = {} _subhints = {}
_hints = resolve_type_hint(hints[k], [dict]) _hints = resolve_type_hint(hints[k], [dict])
hints_flat = list(flatten_hints(_hints)) hints_flat = list(flatten_hints(_hints))
print(f"going over hints for {k}: {_hints=} {hints_flat=}")
subclass = DataClass subclass = DataClass
for hint in hints_flat: for hint in hints_flat:
print(f"working on hint: {hint}")
if get_origin(hint) == dict: if get_origin(hint) == dict:
_valtype = get_args(hint)[1] _valtype = get_args(hint)[1]
_subhints = {n: _valtype for n in v.keys()} _subhints = {n: _valtype for n in v.keys()}
print(f"generated {_subhints=} from {_valtype=}")
break break
if isinstance(hint, type) and issubclass(hint, DataClass): if isinstance(hint, type) and issubclass(hint, DataClass):
subclass = hint subclass = hint
_subhints = hint._type_hints _subhints = hint._type_hints
print(f"found subhints: {_subhints}")
break break
else: else:
print(f"ignoring {hint=}") # print(f"ignoring {hint=}")
print(f"STRIPPING SUBDICT {k=} WITH {_subhints=}") continue
result[k] = subclass.strip_dict( result[k] = subclass.strip_dict(
v, v,
hints=_subhints, hints=_subhints,