dataclass.resolve_type_hint(): add conversion from str to [int,float] if str not in types
This commit is contained in:
parent
2cd41e75ca
commit
040e409620
1 changed files with 26 additions and 6 deletions
32
dataclass.py
32
dataclass.py
|
@ -7,13 +7,13 @@ def munchclass(*args, init=False, **kwargs):
|
||||||
return dataclass(*args, init=init, slots=True, **kwargs)
|
return dataclass(*args, init=init, slots=True, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def resolve_type_hint(hint: type):
|
def resolve_type_hint(hint: type) -> Iterable[type]:
|
||||||
origin = get_origin(hint)
|
origin = get_origin(hint)
|
||||||
args: Iterable[type] = get_args(hint)
|
args: Iterable[type] = get_args(hint)
|
||||||
if origin is Optional:
|
if origin is Optional:
|
||||||
args = set(list(args) + [type(None)])
|
args = set(list(args) + [type(None)])
|
||||||
if origin in [Union, Optional]:
|
if origin in [Union, Optional]:
|
||||||
results = []
|
results: list[type] = []
|
||||||
for arg in args:
|
for arg in args:
|
||||||
results += resolve_type_hint(arg)
|
results += resolve_type_hint(arg)
|
||||||
return results
|
return results
|
||||||
|
@ -33,14 +33,34 @@ class DataClass(Munch):
|
||||||
value = values.pop(key)
|
value = values.pop(key)
|
||||||
type_hints = cls._type_hints
|
type_hints = cls._type_hints
|
||||||
if key in type_hints:
|
if key in type_hints:
|
||||||
_classes = tuple(resolve_type_hint(type_hints[key]))
|
_classes = tuple[type](resolve_type_hint(type_hints[key]))
|
||||||
if issubclass(_classes[0], dict):
|
if issubclass(_classes[0], dict):
|
||||||
assert isinstance(value, dict)
|
assert isinstance(value, dict)
|
||||||
target_class = _classes[0]
|
target_class = _classes[0]
|
||||||
if not issubclass(_classes[0], Munch):
|
if target_class is dict:
|
||||||
target_class = DataClass
|
target_class = Munch
|
||||||
if not isinstance(value, target_class):
|
if not isinstance(value, target_class):
|
||||||
value = target_class.fromDict(value, validate=validate)
|
assert issubclass(target_class, Munch)
|
||||||
|
# despite the above assert, mypy doesn't seem to understand target_class is a Munch here
|
||||||
|
value = target_class.fromDict(value, validate=validate) # type:ignore[attr-defined]
|
||||||
|
# handle numerics
|
||||||
|
elif set(_classes).intersection([int, float]) and isinstance(value, str) and str not in _classes:
|
||||||
|
parsed_number = None
|
||||||
|
parsers: list[tuple[type, list]] = [(int, [10]), (int, [0]), (float, [])]
|
||||||
|
for _cls, args in parsers:
|
||||||
|
if _cls not in _classes:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
parsed_number = _cls(value, *args)
|
||||||
|
break
|
||||||
|
except ValueError:
|
||||||
|
continue
|
||||||
|
if parsed_number is None:
|
||||||
|
if validate:
|
||||||
|
raise Exception(f"Couldn't parse string value {repr(value)} for key '{key}' into number formats: " +
|
||||||
|
(', '.join(list(c.__name__ for c in _classes))))
|
||||||
|
else:
|
||||||
|
value = parsed_number
|
||||||
if validate:
|
if validate:
|
||||||
if not isinstance(value, _classes):
|
if not isinstance(value, _classes):
|
||||||
raise Exception(f'key "{key}" has value of wrong type {_classes}: {value}')
|
raise Exception(f'key "{key}" has value of wrong type {_classes}: {value}')
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue