/
__init__.py
293 lines (248 loc) · 10.1 KB
/
__init__.py
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# -*- coding: utf-8 -*-
from benedict.core import clean as _clean
from benedict.core import clone as _clone
from benedict.core import dump as _dump
from benedict.core import filter as _filter
from benedict.core import find as _find
from benedict.core import flatten as _flatten
from benedict.core import groupby as _groupby
from benedict.core import invert as _invert
from benedict.core import items_sorted_by_keys as _items_sorted_by_keys
from benedict.core import items_sorted_by_values as _items_sorted_by_values
from benedict.core import keypaths as _keypaths
from benedict.core import match as _match
from benedict.core import merge as _merge
from benedict.core import move as _move
from benedict.core import nest as _nest
from benedict.core import remove as _remove
from benedict.core import rename as _rename
from benedict.core import search as _search
from benedict.core import standardize as _standardize
from benedict.core import subset as _subset
from benedict.core import swap as _swap
from benedict.core import traverse as _traverse
from benedict.core import unflatten as _unflatten
from benedict.core import unique as _unique
from benedict.dicts.io import IODict
from benedict.dicts.keylist import KeylistDict
from benedict.dicts.keypath import KeypathDict
from benedict.dicts.parse import ParseDict
# fix benedict json dumps support - #57 #59 #61
from json import encoder
# fix benedict yaml representer - #43
from yaml import SafeDumper
from yaml.representer import SafeRepresenter
__all__ = ["benedict", "IODict", "KeylistDict", "KeypathDict", "ParseDict"]
class benedict(KeypathDict, IODict, ParseDict):
def __init__(self, *args, **kwargs):
"""
Constructs a new instance.
"""
if len(args) == 1 and isinstance(args[0], benedict):
obj = args[0]
kwargs.setdefault("keypath_separator", obj.keypath_separator)
super(benedict, self).__init__(obj.dict(), **kwargs)
return
super(benedict, self).__init__(*args, **kwargs)
def __deepcopy__(self, memo):
obj_type = type(self)
obj = obj_type(keypath_separator=self._keypath_separator)
for key, value in self.items():
obj[key] = _clone(value, memo=memo)
return obj
def __getitem__(self, key):
return self._cast(super(benedict, self).__getitem__(key))
def _cast(self, value):
"""
Cast a dict instance to a benedict instance
keeping the pointer to the original dict.
"""
obj_type = type(self)
if isinstance(value, dict) and not isinstance(value, obj_type):
return obj_type(
value, keypath_separator=self._keypath_separator, check_keys=False
)
return value
def clean(self, strings=True, collections=True):
"""
Clean the current dict instance removing all empty values: None, '', {}, [], ().
If strings or collections (dict, list, set, tuple) flags are False,
related empty values will not be deleted.
"""
_clean(self, strings=strings, collections=collections)
def clone(self):
"""
Creates and return a clone of the current dict instance (deep copy).
"""
return self._cast(_clone(self))
def copy(self):
"""
Creates and return a copy of the current instance (shallow copy).
"""
return self._cast(super(benedict, self).copy())
def deepcopy(self):
"""
Alias of 'clone' method.
"""
return self.clone()
def deepupdate(self, other, *args):
"""
Alias of 'merge' method.
"""
self.merge(other, *args)
def dump(self, data=None):
"""
Return a readable string representation of any dict/list.
This method can be used both as static method or instance method.
"""
return _dump(data or self)
def filter(self, predicate):
"""
Return a new filtered dict using the given predicate function.
Predicate function receives key, value arguments and should return a bool value.
"""
return _filter(self, predicate)
def find(self, keys, default=None):
"""
Return the first match searching for the given keys.
If no result found, default value is returned.
"""
return _find(self, keys, default)
def flatten(self, separator="_"):
"""
Return a new flattened dict using the given separator
to join nested dict keys to flatten keypaths.
"""
if separator == self._keypath_separator:
raise ValueError(
f"Invalid flatten separator: '{separator}', "
"flatten separator must be different from keypath separator."
)
return _flatten(self, separator)
def get(self, key, default=None):
return self._cast(super(benedict, self).get(key, default))
def get_dict(self, key, default=None):
return self._cast(super(benedict, self).get_dict(key, default))
def get_list_item(self, key, index=0, default=None, separator=","):
return self._cast(
super(benedict, self).get_list_item(key, index, default, separator)
)
def groupby(self, key, by_key):
"""
Group a list of dicts at key by the value of the given by_key and return a new dict.
"""
return self._cast(_groupby(self[key], by_key))
def invert(self, flat=False):
"""
Return a new inverted dict, where values become keys and keys become values.
Since multiple keys could have the same value, each value will be a list of keys.
If flat is True each value will be a single value (use this only if values are unique).
"""
return _invert(self, flat)
def items_sorted_by_keys(self, reverse=False):
"""
Return items (key/value list) sorted by keys.
If reverse is True, the list will be reversed.
"""
return _items_sorted_by_keys(self, reverse=reverse)
def items_sorted_by_values(self, reverse=False):
"""
Return items (key/value list) sorted by values.
If reverse is True, the list will be reversed.
"""
return _items_sorted_by_values(self, reverse=reverse)
def keypaths(self, indexes=False):
"""
Return a list of all keypaths in the dict.
If indexes is True, the output will include list values indexes.
"""
return _keypaths(self, separator=self._keypath_separator, indexes=indexes)
def match(self, pattern, indexes=True):
"""
Return a list of all values whose keypath matches the given pattern (a regex or string).
If pattern is string, wildcard can be used (eg. [*] can be used to match all list indexes).
If indexes is True, the pattern will be matched also against list values.
"""
return _match(self, pattern, separator=self._keypath_separator, indexes=indexes)
def merge(self, other, *args, **kwargs):
"""
Merge one or more dict objects into current instance (deepupdate).
Sub-dictionaries will be merged toghether.
If overwrite is False, existing values will not be overwritten.
If concat is True, list values will be concatenated toghether.
"""
_merge(self, other, *args, **kwargs)
def move(self, key_src, key_dest):
"""
Move a dict instance value item from 'key_src' to 'key_dst'.
If key_dst exists, its value will be overwritten.
"""
_move(self, key_src, key_dest)
def nest(
self, key, id_key="id", parent_id_key="parent_id", children_key="children"
):
"""
Nest a list of dicts at the given key and return a new nested list
using the specified keys to establish the correct items hierarchy.
"""
return _nest(self[key], id_key, parent_id_key, children_key)
def pop(self, key, *args):
return self._cast(super(benedict, self).pop(key, *args))
def remove(self, keys, *args):
"""
Remove multiple keys from the current dict instance.
It is possible to pass a single key or more keys (as list or *args).
"""
_remove(self, keys, *args)
def setdefault(self, key, default=None):
return self._cast(super(benedict, self).setdefault(key, default))
def rename(self, key, key_new):
"""
Rename a dict item key from 'key' to 'key_new'.
If key_new exists, a KeyError will be raised.
"""
_rename(self, key, key_new)
def search(
self, query, in_keys=True, in_values=True, exact=False, case_sensitive=False
):
"""
Search and return a list of items (dict, key, value, ) matching the given query.
"""
return _search(self, query, in_keys, in_values, exact, case_sensitive)
def standardize(self):
"""
Standardize all dict keys (e.g. 'Location Latitude' -> 'location_latitude').
"""
_standardize(self)
def subset(self, keys, *args):
"""
Return a new dict subset for the given keys.
It is possible to pass a single key or multiple keys (as list or *args).
"""
return _subset(self, keys, *args)
def swap(self, key1, key2):
"""
Swap items values at the given keys.
"""
_swap(self, key1, key2)
def traverse(self, callback):
"""
Traverse the current dict instance (including nested dicts),
and pass each item (dict, key, value) to the callback function.
"""
_traverse(self, callback)
def unflatten(self, separator="_"):
"""
Return a new unflattened dict using the given separator
to split dict keys to nested keypaths.
"""
return _unflatten(self, separator)
def unique(self):
"""
Remove duplicated values from the current dict instance.
"""
_unique(self)
# fix benedict json dumps support - #57 #59 #61
encoder.c_make_encoder = None
# fix benedict yaml representer - #43
SafeDumper.yaml_representers[benedict] = SafeRepresenter.represent_dict