This repository was archived by the owner on May 13, 2025. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDotDict.py
More file actions
126 lines (101 loc) · 4.4 KB
/
Copy pathDotDict.py
File metadata and controls
126 lines (101 loc) · 4.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import copy
import warnings
import itertools
import overrides
from typing import Any, Dict, Iterable, List
__all__ = ["DotDict"]
class DotDict(dict):
"""
Recursively converts nested dictionaries to dotted dictionary.
"""
_key_error_handling: str
_depth: int = 0
def __init__(self,
in_dict: Dict[str, Any],
depth: int = 0,
**kwargs
) -> None:
"""
:param in_dict: input dictionary
:param key_error_handling: ["raise" | "warn"]
"""
if not isinstance(in_dict, dict):
raise ValueError(f"Incorrect input type: {type(in_dict)}")
self._depth: int = depth
self._key_error_handling: str = kwargs.get("key_error_handling", "raise")
super(DotDict, self).__init__()
for k, v in in_dict.items():
k: str = self._preprocess_key(k, depth, kwargs.get("capitalize_first_level_key", True if depth == 0 else False))
if isinstance(v, (list, tuple, set)):
# v = self._remove_duplicated_dicts(v) # don't remember why I have to remove duplications :v
setattr(self, k, [DotDict(x, depth + 1, **kwargs) if isinstance(x, dict) else x for x in v])
else:
setattr(self, k, DotDict(v, depth + 1, **kwargs) if isinstance(v, dict) else v)
def __getattr__(self, k: str) -> Any:
try:
return self[self._preprocess_key(k, self._depth-1)]
except KeyError as e:
if self._key_error_handling == "raise":
raise e
elif self._key_error_handling == "warn":
warnings.warn(f"{self.__class__.__name__} has no attribute '{k}' so returns None")
def __dict__(self) -> Dict[str, Any]:
return self._parse_dict(dict(self))
@overrides.override
def __setattr__(self, k: str, v: Any) -> None:
self[k] = v
@overrides.override
def __delattr__(self, k: str) -> None:
try:
del self[k]
except KeyError:
raise AttributeError(f"{self.__class__.__name__} object has no attribute '{k}'")
def __deepcopy__(self, memo=None) -> object:
new_obj: DotDict = type(self)({}) # Other syntax: self.__class__.__new__(self.__class__)
memo[id(self)] = new_obj
for k, v in self.items():
setattr(new_obj, k, copy.deepcopy(v, memo))
return new_obj
@staticmethod
def _preprocess_key(k: str, depth: int, capitalize_first_level_key: bool = False) -> str:
k = k.replace("-", "_")
k = k.replace(" ", "_")
k = k.capitalize() if capitalize_first_level_key or depth == 0 else k
return k
@staticmethod
def _remove_duplicated_dicts(iterable: Iterable) -> List[Any]:
dict_eles: List[Dict[str, Any]] = [x for x in iterable if isinstance(x, dict)]
dict_eles = [ele[0] for ele in itertools.groupby(dict_eles)]
non_dict_eles: List[Any] = [x for x in iterable if not isinstance(x, dict)]
result: List[Any] = [*dict_eles, *non_dict_eles]
return result
@staticmethod
def _is_public(attr_name: str) -> bool:
if not (attr_name.startswith("_") or attr_name.startswith("__")):
return True
else:
return False
# TODO: being duplicated with get_dict. Fix later on
def _parse_dict(self, in_dict: Dict[str, Any]) -> Dict[str, Any]:
parsed_dict: Dict[str, Any] = {}
for k, v in in_dict.items():
if self._is_public(k):
if isinstance(v, dict):
v: Dict[str, Any] = self._parse_dict(v)
elif isinstance(v, (tuple, list)):
v: List[Any] = [self._parse_dict(x) if isinstance(x, dict) else x for x in v]
parsed_dict[k] = v
return parsed_dict
def get_dict(self, k: str = None) -> Dict[str, Any]:
return_dict: Dict[str, Any] = {}
if k is not None and self.get(k) is not None:
for k, v in self[k].items():
if self._is_public(k):
if isinstance(v, dict):
v: Dict[str, Any] = self._parse_dict(v)
elif isinstance(v, (tuple, list)):
v: List[Any] = [self._parse_dict(x) if isinstance(x, dict) else x for x in v]
return_dict[k] = v
else:
return_dict = self.__dict__()
return return_dict