56 lines
1.5 KiB
Python
56 lines
1.5 KiB
Python
|
"""Utilities for diot"""
|
||
|
from typing import TYPE_CHECKING, Any, Iterable
|
||
|
|
||
|
|
||
|
if TYPE_CHECKING:
|
||
|
from .diot import Diot
|
||
|
|
||
|
|
||
|
class DiotFrozenError(Exception):
|
||
|
"""When try to modify a frozen diot"""
|
||
|
|
||
|
|
||
|
def nest(
|
||
|
value: Any,
|
||
|
types: Iterable[type],
|
||
|
dest_type: type,
|
||
|
frozen: bool,
|
||
|
) -> Any:
|
||
|
"""Convert values with certain types recursively"""
|
||
|
# nothing to convert
|
||
|
if not types or not isinstance(value, tuple(types)): # type: ignore
|
||
|
return value
|
||
|
|
||
|
if (list in types and isinstance(value, list)) or ( # type: ignore
|
||
|
tuple in types and isinstance(value, tuple) # type: ignore
|
||
|
):
|
||
|
# use value.__class__ to keep user-subclassed list or tuple
|
||
|
out = [nest(val, types, dest_type, frozen) for val in value]
|
||
|
try:
|
||
|
return value.__class__(out)
|
||
|
except Exception: # pragma: no cover
|
||
|
return out
|
||
|
|
||
|
if dict in types and isinstance(value, dict): # type: ignore
|
||
|
if issubclass(value.__class__, dest_type):
|
||
|
return value
|
||
|
|
||
|
return dest_type(
|
||
|
[
|
||
|
(key, nest(val, types, dest_type, frozen))
|
||
|
for key, val in value.items()
|
||
|
]
|
||
|
)
|
||
|
return value
|
||
|
|
||
|
|
||
|
def to_dict(value: "Diot") -> dict:
|
||
|
"""Convert converted Diot objects back to dict"""
|
||
|
if isinstance(value, dict):
|
||
|
return {key: to_dict(val) for key, val in value.items()}
|
||
|
if isinstance(value, tuple):
|
||
|
return tuple((to_dict(val) for val in value))
|
||
|
if isinstance(value, list):
|
||
|
return [to_dict(val) for val in value]
|
||
|
return value
|