python-benedict 0.34.0__tar.gz → 0.35.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_benedict-0.34.0/python_benedict.egg-info → python_benedict-0.35.0}/PKG-INFO +12 -8
- {python_benedict-0.34.0 → python_benedict-0.35.0}/README.md +4 -4
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/clean.py +28 -10
- python_benedict-0.35.0/benedict/core/clone.py +18 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/dump.py +3 -1
- python_benedict-0.35.0/benedict/core/filter.py +21 -0
- python_benedict-0.35.0/benedict/core/find.py +16 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/flatten.py +13 -6
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/groupby.py +10 -2
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/invert.py +12 -4
- python_benedict-0.35.0/benedict/core/items_sorted.py +25 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/keylists.py +17 -4
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/keypaths.py +13 -2
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/match.py +10 -1
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/merge.py +24 -5
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/move.py +9 -1
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/nest.py +17 -2
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/remove.py +4 -1
- python_benedict-0.35.0/benedict/core/rename.py +10 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/search.py +24 -5
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/standardize.py +5 -3
- python_benedict-0.35.0/benedict/core/subset.py +24 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/swap.py +7 -1
- python_benedict-0.35.0/benedict/core/traverse.py +46 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/unflatten.py +8 -3
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/unique.py +5 -1
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/__init__.py +100 -62
- python_benedict-0.35.0/benedict/dicts/base/base_dict.py +178 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/io/io_dict.py +75 -47
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/io/io_util.py +44 -23
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keyattr/keyattr_dict.py +30 -18
- python_benedict-0.35.0/benedict/dicts/keylist/keylist_dict.py +116 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keylist/keylist_util.py +21 -9
- python_benedict-0.35.0/benedict/dicts/keypath/keypath_dict.py +77 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keypath/keypath_util.py +12 -8
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/parse/parse_dict.py +200 -60
- python_benedict-0.35.0/benedict/dicts/parse/parse_util.py +344 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/exceptions.py +1 -1
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/extras.py +8 -8
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/metadata.py +1 -1
- python_benedict-0.35.0/benedict/py.typed +0 -0
- python_benedict-0.35.0/benedict/serializers/__init__.py +194 -0
- python_benedict-0.35.0/benedict/serializers/abstract.py +27 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/base64.py +23 -13
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/cli.py +13 -8
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/csv.py +8 -5
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/html.py +8 -4
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/ini.py +20 -9
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/json.py +10 -7
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/pickle.py +10 -4
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/plist.py +5 -4
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/query_string.py +20 -6
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/toml.py +6 -4
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/xls.py +21 -13
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/xml.py +9 -5
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/yaml.py +9 -7
- python_benedict-0.35.0/benedict/utils/type_util.py +109 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/pyproject.toml +29 -3
- {python_benedict-0.34.0 → python_benedict-0.35.0/python_benedict.egg-info}/PKG-INFO +12 -8
- {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/SOURCES.txt +1 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/requires.txt +4 -2
- python_benedict-0.34.0/benedict/core/clone.py +0 -8
- python_benedict-0.34.0/benedict/core/filter.py +0 -13
- python_benedict-0.34.0/benedict/core/find.py +0 -5
- python_benedict-0.34.0/benedict/core/items_sorted.py +0 -10
- python_benedict-0.34.0/benedict/core/rename.py +0 -5
- python_benedict-0.34.0/benedict/core/subset.py +0 -12
- python_benedict-0.34.0/benedict/core/traverse.py +0 -29
- python_benedict-0.34.0/benedict/dicts/base/base_dict.py +0 -163
- python_benedict-0.34.0/benedict/dicts/keylist/keylist_dict.py +0 -95
- python_benedict-0.34.0/benedict/dicts/keypath/keypath_dict.py +0 -65
- python_benedict-0.34.0/benedict/dicts/parse/parse_util.py +0 -231
- python_benedict-0.34.0/benedict/serializers/__init__.py +0 -88
- python_benedict-0.34.0/benedict/serializers/abstract.py +0 -17
- python_benedict-0.34.0/benedict/utils/type_util.py +0 -95
- {python_benedict-0.34.0 → python_benedict-0.35.0}/LICENSE.txt +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/MANIFEST.in +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/base/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/io/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keyattr/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keylist/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keypath/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/parse/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/utils/__init__.py +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/dependency_links.txt +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/top_level.txt +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/setup.cfg +0 -0
- {python_benedict-0.34.0 → python_benedict-0.35.0}/setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-benedict
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.35.0
|
|
4
4
|
Summary: python-benedict is a dict subclass with keylist/keypath/keyattr support, normalized I/O operations (base64, csv, ini, json, pickle, plist, query-string, toml, xls, xml, yaml) and many utilities... for humans, obviously.
|
|
5
5
|
Author-email: Fabio Caccamo <fabio.caccamo@gmail.com>
|
|
6
6
|
Maintainer-email: Fabio Caccamo <fabio.caccamo@gmail.com>
|
|
@@ -56,11 +56,14 @@ Classifier: Topic :: Software Development :: Build Tools
|
|
|
56
56
|
Classifier: Topic :: System :: Filesystems
|
|
57
57
|
Classifier: Topic :: Text Processing :: Markup :: XML
|
|
58
58
|
Classifier: Topic :: Utilities
|
|
59
|
+
Classifier: Typing :: Typed
|
|
59
60
|
Description-Content-Type: text/markdown
|
|
60
61
|
License-File: LICENSE.txt
|
|
61
62
|
Requires-Dist: python-fsutil<1.0.0,>=0.9.3
|
|
62
63
|
Requires-Dist: python-slugify<9.0.0,>=7.0.0
|
|
63
64
|
Requires-Dist: requests<3.0.0,>=2.26.0
|
|
65
|
+
Requires-Dist: typing_extensions<4.16.0,>=4.13.2
|
|
66
|
+
Requires-Dist: useful-types<0.3.0,>=0.2.1
|
|
64
67
|
Provides-Extra: all
|
|
65
68
|
Requires-Dist: python-benedict[io,parse,s3]; extra == "all"
|
|
66
69
|
Provides-Extra: html
|
|
@@ -71,7 +74,7 @@ Requires-Dist: python-benedict[html,toml,xls,xml,yaml]; extra == "io"
|
|
|
71
74
|
Provides-Extra: parse
|
|
72
75
|
Requires-Dist: ftfy<7.0.0,>=6.0.0; extra == "parse"
|
|
73
76
|
Requires-Dist: mailchecker<7.0.0,>=4.1.0; extra == "parse"
|
|
74
|
-
Requires-Dist: phonenumbers<
|
|
77
|
+
Requires-Dist: phonenumbers<10.0.0,>=8.12.0; extra == "parse"
|
|
75
78
|
Requires-Dist: python-dateutil<3.0.0,>=2.8.0; extra == "parse"
|
|
76
79
|
Provides-Extra: s3
|
|
77
80
|
Requires-Dist: boto3<2.0.0,>=1.24.89; extra == "s3"
|
|
@@ -81,9 +84,10 @@ Provides-Extra: xls
|
|
|
81
84
|
Requires-Dist: openpyxl<4.0.0,>=3.0.0; extra == "xls"
|
|
82
85
|
Requires-Dist: xlrd<3.0.0,>=2.0.0; extra == "xls"
|
|
83
86
|
Provides-Extra: xml
|
|
84
|
-
Requires-Dist: xmltodict<
|
|
87
|
+
Requires-Dist: xmltodict<2.0.0,>=0.12.0; extra == "xml"
|
|
85
88
|
Provides-Extra: yaml
|
|
86
89
|
Requires-Dist: pyyaml<7.0,>=6.0; extra == "yaml"
|
|
90
|
+
Dynamic: license-file
|
|
87
91
|
|
|
88
92
|
[](https://www.python.org/)
|
|
89
93
|
[](https://pypi.org/project/python-benedict/)
|
|
@@ -94,7 +98,6 @@ Requires-Dist: pyyaml<7.0,>=6.0; extra == "yaml"
|
|
|
94
98
|
[](https://results.pre-commit.ci/latest/github/fabiocaccamo/python-benedict/main)
|
|
95
99
|
[](https://github.com/fabiocaccamo/python-benedict)
|
|
96
100
|
[](https://codecov.io/gh/fabiocaccamo/python-benedict)
|
|
97
|
-
[](https://codeclimate.com/github/fabiocaccamo/python-benedict/)
|
|
98
101
|
[](https://www.codacy.com/app/fabiocaccamo/python-benedict)
|
|
99
102
|
[](https://scrutinizer-ci.com/g/fabiocaccamo/python-benedict/?branch=main)
|
|
100
103
|
[](https://github.com/psf/black)
|
|
@@ -217,7 +220,7 @@ d.keyattr_dynamic = True
|
|
|
217
220
|
> **Warning** - even if this feature is very useful, it has some obvious limitations: it works only for string keys that are *unprotected* (not starting with an `_`) and that don't clash with the currently supported methods names.
|
|
218
221
|
|
|
219
222
|
### Keylist
|
|
220
|
-
Wherever a **key** is used, it is possible to use also a **list
|
|
223
|
+
Wherever a **key** is used, it is possible to use also a **list of keys**.
|
|
221
224
|
|
|
222
225
|
```python
|
|
223
226
|
d = benedict()
|
|
@@ -545,7 +548,8 @@ items = d.items_sorted_by_values(reverse=False)
|
|
|
545
548
|
```python
|
|
546
549
|
# Return a list of all keypaths in the dict.
|
|
547
550
|
# If indexes is True, the output will include list values indexes.
|
|
548
|
-
|
|
551
|
+
# If sort is True, the resulting list will be sorted
|
|
552
|
+
k = d.keypaths(indexes=False, sort=True)
|
|
549
553
|
```
|
|
550
554
|
|
|
551
555
|
#### `match`
|
|
@@ -1126,7 +1130,7 @@ Released under [MIT License](LICENSE.txt).
|
|
|
1126
1130
|
|
|
1127
1131
|
- :star: Star this project on [GitHub](https://github.com/fabiocaccamo/python-benedict)
|
|
1128
1132
|
- :octocat: Follow me on [GitHub](https://github.com/fabiocaccamo)
|
|
1129
|
-
- :blue_heart: Follow me on [
|
|
1133
|
+
- :blue_heart: Follow me on [Bluesky](https://bsky.app/profile/fabiocaccamo.bsky.social)
|
|
1130
1134
|
- :moneybag: Sponsor me on [Github](https://github.com/sponsors/fabiocaccamo)
|
|
1131
1135
|
|
|
1132
1136
|
## See also
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
[](https://results.pre-commit.ci/latest/github/fabiocaccamo/python-benedict/main)
|
|
8
8
|
[](https://github.com/fabiocaccamo/python-benedict)
|
|
9
9
|
[](https://codecov.io/gh/fabiocaccamo/python-benedict)
|
|
10
|
-
[](https://codeclimate.com/github/fabiocaccamo/python-benedict/)
|
|
11
10
|
[](https://www.codacy.com/app/fabiocaccamo/python-benedict)
|
|
12
11
|
[](https://scrutinizer-ci.com/g/fabiocaccamo/python-benedict/?branch=main)
|
|
13
12
|
[](https://github.com/psf/black)
|
|
@@ -130,7 +129,7 @@ d.keyattr_dynamic = True
|
|
|
130
129
|
> **Warning** - even if this feature is very useful, it has some obvious limitations: it works only for string keys that are *unprotected* (not starting with an `_`) and that don't clash with the currently supported methods names.
|
|
131
130
|
|
|
132
131
|
### Keylist
|
|
133
|
-
Wherever a **key** is used, it is possible to use also a **list
|
|
132
|
+
Wherever a **key** is used, it is possible to use also a **list of keys**.
|
|
134
133
|
|
|
135
134
|
```python
|
|
136
135
|
d = benedict()
|
|
@@ -458,7 +457,8 @@ items = d.items_sorted_by_values(reverse=False)
|
|
|
458
457
|
```python
|
|
459
458
|
# Return a list of all keypaths in the dict.
|
|
460
459
|
# If indexes is True, the output will include list values indexes.
|
|
461
|
-
|
|
460
|
+
# If sort is True, the resulting list will be sorted
|
|
461
|
+
k = d.keypaths(indexes=False, sort=True)
|
|
462
462
|
```
|
|
463
463
|
|
|
464
464
|
#### `match`
|
|
@@ -1039,7 +1039,7 @@ Released under [MIT License](LICENSE.txt).
|
|
|
1039
1039
|
|
|
1040
1040
|
- :star: Star this project on [GitHub](https://github.com/fabiocaccamo/python-benedict)
|
|
1041
1041
|
- :octocat: Follow me on [GitHub](https://github.com/fabiocaccamo)
|
|
1042
|
-
- :blue_heart: Follow me on [
|
|
1042
|
+
- :blue_heart: Follow me on [Bluesky](https://bsky.app/profile/fabiocaccamo.bsky.social)
|
|
1043
1043
|
- :moneybag: Sponsor me on [Github](https://github.com/sponsors/fabiocaccamo)
|
|
1044
1044
|
|
|
1045
1045
|
## See also
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import MutableMapping, MutableSequence
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
_K = TypeVar("_K")
|
|
7
|
+
_V = TypeVar("_V")
|
|
8
|
+
_T = TypeVar("_T")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _clean_dict(
|
|
12
|
+
d: MutableMapping[_K, _V], strings: bool, collections: bool
|
|
13
|
+
) -> MutableMapping[_K, _V]:
|
|
2
14
|
keys = list(d.keys())
|
|
3
15
|
for key in keys:
|
|
4
16
|
d[key] = _clean_value(d[key], strings=strings, collections=collections)
|
|
@@ -7,7 +19,9 @@ def _clean_dict(d, strings, collections):
|
|
|
7
19
|
return d
|
|
8
20
|
|
|
9
21
|
|
|
10
|
-
def _clean_list(
|
|
22
|
+
def _clean_list(
|
|
23
|
+
ls: MutableSequence[_T], strings: bool, collections: bool
|
|
24
|
+
) -> MutableSequence[_T]:
|
|
11
25
|
for i in range(len(ls) - 1, -1, -1):
|
|
12
26
|
ls[i] = _clean_value(ls[i], strings=strings, collections=collections)
|
|
13
27
|
if ls[i] is None:
|
|
@@ -15,7 +29,7 @@ def _clean_list(ls, strings, collections):
|
|
|
15
29
|
return ls
|
|
16
30
|
|
|
17
31
|
|
|
18
|
-
def _clean_set(values, strings, collections):
|
|
32
|
+
def _clean_set(values: set[_T], strings: bool, collections: bool) -> set[_T]:
|
|
19
33
|
return {
|
|
20
34
|
value
|
|
21
35
|
for value in values
|
|
@@ -23,11 +37,13 @@ def _clean_set(values, strings, collections):
|
|
|
23
37
|
}
|
|
24
38
|
|
|
25
39
|
|
|
26
|
-
def _clean_str(s, strings, collections):
|
|
40
|
+
def _clean_str(s: str, strings: bool, collections: bool) -> str | None:
|
|
27
41
|
return s if s and s.strip() else None
|
|
28
42
|
|
|
29
43
|
|
|
30
|
-
def _clean_tuple(
|
|
44
|
+
def _clean_tuple(
|
|
45
|
+
values: tuple[_T, ...], strings: bool, collections: bool
|
|
46
|
+
) -> tuple[_T, ...]:
|
|
31
47
|
return tuple(
|
|
32
48
|
value
|
|
33
49
|
for value in values
|
|
@@ -35,13 +51,15 @@ def _clean_tuple(values, strings, collections):
|
|
|
35
51
|
)
|
|
36
52
|
|
|
37
53
|
|
|
38
|
-
def _clean_value(value, strings, collections):
|
|
54
|
+
def _clean_value(value: Any, strings: bool, collections: bool) -> Any:
|
|
39
55
|
if value is None:
|
|
40
56
|
return value
|
|
41
|
-
elif isinstance(value,
|
|
57
|
+
elif isinstance(value, MutableSequence) and collections:
|
|
42
58
|
value = _clean_list(value, strings=strings, collections=collections) or None
|
|
43
|
-
elif isinstance(value,
|
|
44
|
-
value =
|
|
59
|
+
elif isinstance(value, MutableMapping) and collections:
|
|
60
|
+
value = (
|
|
61
|
+
_clean_dict(dict(value), strings=strings, collections=collections) or None
|
|
62
|
+
)
|
|
45
63
|
elif isinstance(value, set) and collections:
|
|
46
64
|
value = _clean_set(value, strings=strings, collections=collections) or None
|
|
47
65
|
elif isinstance(value, str) and strings:
|
|
@@ -51,5 +69,5 @@ def _clean_value(value, strings, collections):
|
|
|
51
69
|
return value
|
|
52
70
|
|
|
53
71
|
|
|
54
|
-
def clean(d, strings=True, collections=True):
|
|
72
|
+
def clean(d: Any, strings: bool = True, collections: bool = True) -> Any:
|
|
55
73
|
return _clean_dict(d, strings=strings, collections=collections)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import copy
|
|
4
|
+
from collections.abc import MutableMapping
|
|
5
|
+
from typing import Any, TypeVar
|
|
6
|
+
|
|
7
|
+
_T = TypeVar("_T")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def clone(
|
|
11
|
+
obj: _T,
|
|
12
|
+
empty: bool = False,
|
|
13
|
+
memo: dict[int, Any] | None = None,
|
|
14
|
+
) -> _T:
|
|
15
|
+
d = copy.deepcopy(obj, memo)
|
|
16
|
+
if empty and isinstance(d, MutableMapping):
|
|
17
|
+
d.clear()
|
|
18
|
+
return d
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from collections.abc import Callable, MutableMapping
|
|
2
|
+
from typing import TypeVar
|
|
3
|
+
|
|
4
|
+
from benedict.core.clone import clone
|
|
5
|
+
|
|
6
|
+
_K = TypeVar("_K")
|
|
7
|
+
_V = TypeVar("_V")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def filter(
|
|
11
|
+
d: MutableMapping[_K, _V], predicate: Callable[[_K, _V], bool]
|
|
12
|
+
) -> MutableMapping[_K, _V]:
|
|
13
|
+
if not callable(predicate):
|
|
14
|
+
raise ValueError("predicate argument must be a callable.")
|
|
15
|
+
new_dict = clone(d, empty=True)
|
|
16
|
+
keys = list(d.keys())
|
|
17
|
+
for key in keys:
|
|
18
|
+
value = d[key]
|
|
19
|
+
if predicate(key, value):
|
|
20
|
+
new_dict[key] = value
|
|
21
|
+
return new_dict
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterable, Mapping
|
|
4
|
+
from typing import TypeVar
|
|
5
|
+
|
|
6
|
+
_K = TypeVar("_K")
|
|
7
|
+
_V = TypeVar("_V")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def find(
|
|
11
|
+
d: Mapping[_K, _V], keys: Iterable[_K], default: _V | None = None
|
|
12
|
+
) -> _V | None:
|
|
13
|
+
for key in keys:
|
|
14
|
+
if key in d:
|
|
15
|
+
return d[key]
|
|
16
|
+
return default
|
|
@@ -1,20 +1,27 @@
|
|
|
1
|
-
from
|
|
2
|
-
from
|
|
1
|
+
from collections.abc import Mapping
|
|
2
|
+
from typing import Any
|
|
3
3
|
|
|
4
|
+
from benedict.core.clone import clone
|
|
4
5
|
|
|
5
|
-
|
|
6
|
+
|
|
7
|
+
def _flatten_key(base_key: str, key: str, separator: str) -> str:
|
|
6
8
|
if base_key and separator:
|
|
7
9
|
return f"{base_key}{separator}{key}"
|
|
8
10
|
return key
|
|
9
11
|
|
|
10
12
|
|
|
11
|
-
def _flatten_item(
|
|
13
|
+
def _flatten_item(
|
|
14
|
+
d: Any,
|
|
15
|
+
base_dict: Any,
|
|
16
|
+
base_key: str,
|
|
17
|
+
separator: str,
|
|
18
|
+
) -> Any:
|
|
12
19
|
new_dict = base_dict
|
|
13
20
|
keys = list(d.keys())
|
|
14
21
|
for key in keys:
|
|
15
22
|
new_key = _flatten_key(base_key, key, separator)
|
|
16
23
|
value = d.get(key, None)
|
|
17
|
-
if
|
|
24
|
+
if isinstance(value, Mapping):
|
|
18
25
|
new_value = _flatten_item(
|
|
19
26
|
value, base_dict=new_dict, base_key=new_key, separator=separator
|
|
20
27
|
)
|
|
@@ -26,6 +33,6 @@ def _flatten_item(d, base_dict, base_key, separator):
|
|
|
26
33
|
return new_dict
|
|
27
34
|
|
|
28
35
|
|
|
29
|
-
def flatten(d, separator="_"):
|
|
36
|
+
def flatten(d: Any, separator: str = "_") -> Any:
|
|
30
37
|
new_dict = clone(d, empty=True)
|
|
31
38
|
return _flatten_item(d, base_dict=new_dict, base_key="", separator=separator)
|
|
@@ -1,10 +1,18 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, MutableSequence, Sequence
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
1
6
|
from benedict.utils import type_util
|
|
2
7
|
|
|
8
|
+
_K = TypeVar("_K")
|
|
9
|
+
_V = TypeVar("_V", bound=MutableSequence[Any])
|
|
10
|
+
|
|
3
11
|
|
|
4
|
-
def groupby(items, key):
|
|
12
|
+
def groupby(items: Sequence[Mapping[_K, Any]], key: _K) -> dict[Any, Any]:
|
|
5
13
|
if not type_util.is_list(items):
|
|
6
14
|
raise ValueError("items should be a list of dicts.")
|
|
7
|
-
items_grouped = {}
|
|
15
|
+
items_grouped: dict[Any, Any] = {}
|
|
8
16
|
for item in items:
|
|
9
17
|
if not type_util.is_dict(item):
|
|
10
18
|
raise ValueError("item should be a dict.")
|
|
@@ -1,20 +1,28 @@
|
|
|
1
|
-
from
|
|
1
|
+
from collections.abc import MutableMapping, Sequence
|
|
2
|
+
from typing import Any, TypeVar
|
|
3
|
+
|
|
4
|
+
from benedict.core.clone import clone
|
|
2
5
|
from benedict.utils import type_util
|
|
3
6
|
|
|
7
|
+
_K = TypeVar("_K")
|
|
8
|
+
_V = TypeVar("_V")
|
|
9
|
+
|
|
4
10
|
|
|
5
|
-
def _invert_item(d, key, value, flat):
|
|
11
|
+
def _invert_item(d: MutableMapping[Any, Any], key: _K, value: _V, flat: bool) -> None:
|
|
6
12
|
if flat:
|
|
7
13
|
d.setdefault(value, key)
|
|
8
14
|
else:
|
|
9
15
|
d.setdefault(value, []).append(key)
|
|
10
16
|
|
|
11
17
|
|
|
12
|
-
def _invert_list(
|
|
18
|
+
def _invert_list(
|
|
19
|
+
d: MutableMapping[Any, Any], key: _K, value: Sequence[Any], flat: bool
|
|
20
|
+
) -> None:
|
|
13
21
|
for value_item in value:
|
|
14
22
|
_invert_item(d, key, value_item, flat)
|
|
15
23
|
|
|
16
24
|
|
|
17
|
-
def invert(d, flat=False):
|
|
25
|
+
def invert(d: MutableMapping[_K, _V], flat: bool = False) -> MutableMapping[Any, Any]:
|
|
18
26
|
new_dict = clone(d, empty=True)
|
|
19
27
|
for key, value in d.items():
|
|
20
28
|
if type_util.is_list_or_tuple(value):
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
|
|
5
|
+
from useful_types import SupportsRichComparisonT
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def _items_sorted_by_item_at_index(
|
|
9
|
+
d: Mapping[SupportsRichComparisonT, SupportsRichComparisonT],
|
|
10
|
+
index: int,
|
|
11
|
+
reverse: bool,
|
|
12
|
+
) -> list[tuple[SupportsRichComparisonT, SupportsRichComparisonT]]:
|
|
13
|
+
return sorted(d.items(), key=lambda item: item[index], reverse=reverse)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def items_sorted_by_keys(
|
|
17
|
+
d: Mapping[SupportsRichComparisonT, SupportsRichComparisonT], reverse: bool = False
|
|
18
|
+
) -> list[tuple[SupportsRichComparisonT, SupportsRichComparisonT]]:
|
|
19
|
+
return _items_sorted_by_item_at_index(d, 0, reverse)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def items_sorted_by_values(
|
|
23
|
+
d: Mapping[SupportsRichComparisonT, SupportsRichComparisonT], reverse: bool = False
|
|
24
|
+
) -> list[tuple[SupportsRichComparisonT, SupportsRichComparisonT]]:
|
|
25
|
+
return _items_sorted_by_item_at_index(d, 1, reverse)
|
|
@@ -1,7 +1,14 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, Sequence
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
1
6
|
from benedict.utils import type_util
|
|
2
7
|
|
|
3
8
|
|
|
4
|
-
def _get_keylist_for_dict(
|
|
9
|
+
def _get_keylist_for_dict(
|
|
10
|
+
d: Mapping[Any, Any], parent_keys: list[Any], indexes: bool
|
|
11
|
+
) -> list[list[Any]]:
|
|
5
12
|
keylist = []
|
|
6
13
|
for key, value in d.items():
|
|
7
14
|
keys = parent_keys + [key]
|
|
@@ -10,7 +17,9 @@ def _get_keylist_for_dict(d, parent_keys, indexes):
|
|
|
10
17
|
return keylist
|
|
11
18
|
|
|
12
19
|
|
|
13
|
-
def _get_keylist_for_list(
|
|
20
|
+
def _get_keylist_for_list(
|
|
21
|
+
ls: Sequence[Any], parent_keys: list[Any], indexes: bool
|
|
22
|
+
) -> list[list[Any]]:
|
|
14
23
|
keylist = []
|
|
15
24
|
for key, value in enumerate(ls):
|
|
16
25
|
keys = list(parent_keys)
|
|
@@ -20,7 +29,9 @@ def _get_keylist_for_list(ls, parent_keys, indexes):
|
|
|
20
29
|
return keylist
|
|
21
30
|
|
|
22
31
|
|
|
23
|
-
def _get_keylist_for_value(
|
|
32
|
+
def _get_keylist_for_value(
|
|
33
|
+
value: Mapping[Any, Any] | Sequence[Any], parent_keys: list[Any], indexes: bool
|
|
34
|
+
) -> list[list[Any]]:
|
|
24
35
|
if type_util.is_dict(value):
|
|
25
36
|
return _get_keylist_for_dict(value, parent_keys, indexes)
|
|
26
37
|
elif type_util.is_list(value) and indexes:
|
|
@@ -28,5 +39,7 @@ def _get_keylist_for_value(value, parent_keys, indexes):
|
|
|
28
39
|
return []
|
|
29
40
|
|
|
30
41
|
|
|
31
|
-
def keylists(
|
|
42
|
+
def keylists(
|
|
43
|
+
d: Mapping[Any, Any] | Sequence[Any], indexes: bool = False
|
|
44
|
+
) -> list[list[Any]]:
|
|
32
45
|
return _get_keylist_for_value(d, [], indexes)
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
1
6
|
from benedict.core.keylists import keylists
|
|
2
7
|
from benedict.utils import type_util
|
|
3
8
|
|
|
4
9
|
|
|
5
|
-
def keypaths(
|
|
10
|
+
def keypaths(
|
|
11
|
+
d: Mapping[Any, Any],
|
|
12
|
+
separator: str | None = ".",
|
|
13
|
+
indexes: bool = False,
|
|
14
|
+
sort: bool = True,
|
|
15
|
+
) -> list[str]:
|
|
6
16
|
separator = separator or "."
|
|
7
17
|
if not type_util.is_string(separator):
|
|
8
18
|
raise ValueError("separator argument must be a (non-empty) string.")
|
|
9
19
|
kls = keylists(d, indexes=indexes)
|
|
10
20
|
kps = [separator.join([f"{key}" for key in kl]) for kl in kls]
|
|
11
|
-
|
|
21
|
+
if sort:
|
|
22
|
+
kps.sort()
|
|
12
23
|
return kps
|
|
@@ -1,10 +1,19 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
1
3
|
import re
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from typing import Any
|
|
2
6
|
|
|
3
7
|
from benedict.core.keypaths import keypaths
|
|
4
8
|
from benedict.utils import type_util
|
|
5
9
|
|
|
6
10
|
|
|
7
|
-
def match(
|
|
11
|
+
def match(
|
|
12
|
+
d: Mapping[Any, Any],
|
|
13
|
+
pattern: str | re.Pattern[str],
|
|
14
|
+
separator: str | None = ".",
|
|
15
|
+
indexes: bool = True,
|
|
16
|
+
) -> list[Any]:
|
|
8
17
|
if type_util.is_regex(pattern):
|
|
9
18
|
regex = pattern
|
|
10
19
|
elif type_util.is_string(pattern):
|
|
@@ -1,25 +1,44 @@
|
|
|
1
|
+
from collections.abc import Mapping, MutableMapping
|
|
2
|
+
from typing import Any, TypeVar
|
|
3
|
+
|
|
1
4
|
from benedict.utils import type_util
|
|
2
5
|
|
|
6
|
+
_K = TypeVar("_K")
|
|
7
|
+
_V = TypeVar("_V")
|
|
8
|
+
|
|
3
9
|
|
|
4
|
-
def _merge_dict(
|
|
10
|
+
def _merge_dict(
|
|
11
|
+
d: MutableMapping[_K, _V],
|
|
12
|
+
other: Mapping[_K, _V],
|
|
13
|
+
overwrite: bool = True,
|
|
14
|
+
concat: bool = False,
|
|
15
|
+
) -> None:
|
|
5
16
|
for key, value in other.items():
|
|
6
17
|
_merge_item(d, key, value, overwrite=overwrite, concat=concat)
|
|
7
18
|
|
|
8
19
|
|
|
9
|
-
def _merge_item(
|
|
20
|
+
def _merge_item(
|
|
21
|
+
d: MutableMapping[_K, _V],
|
|
22
|
+
key: _K,
|
|
23
|
+
value: _V,
|
|
24
|
+
overwrite: bool = True,
|
|
25
|
+
concat: bool = False,
|
|
26
|
+
) -> None:
|
|
10
27
|
if key in d:
|
|
11
|
-
item = d.get(key
|
|
28
|
+
item = d.get(key)
|
|
12
29
|
if type_util.is_dict(item) and type_util.is_dict(value):
|
|
13
30
|
_merge_dict(item, value, overwrite=overwrite, concat=concat)
|
|
14
31
|
elif concat and type_util.is_list(item) and type_util.is_list(value):
|
|
15
|
-
item += value
|
|
32
|
+
item += value # type: ignore[assignment]
|
|
16
33
|
elif overwrite:
|
|
17
34
|
d[key] = value
|
|
18
35
|
else:
|
|
19
36
|
d[key] = value
|
|
20
37
|
|
|
21
38
|
|
|
22
|
-
def merge(
|
|
39
|
+
def merge(
|
|
40
|
+
d: MutableMapping[_K, _V], other: Mapping[_K, _V], *args: Any, **kwargs: Any
|
|
41
|
+
) -> Any:
|
|
23
42
|
overwrite = kwargs.get("overwrite", True)
|
|
24
43
|
concat = kwargs.get("concat", False)
|
|
25
44
|
others = [other] + list(args)
|
|
@@ -1,4 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
from collections.abc import MutableMapping
|
|
2
|
+
from typing import Any, TypeVar
|
|
3
|
+
|
|
4
|
+
_K = TypeVar("_K")
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def move(
|
|
8
|
+
d: MutableMapping[_K, Any], key_src: _K, key_dest: _K, overwrite: bool = True
|
|
9
|
+
) -> None:
|
|
2
10
|
if key_dest == key_src:
|
|
3
11
|
return
|
|
4
12
|
if key_dest in d and not overwrite:
|
|
@@ -1,14 +1,29 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
1
6
|
from benedict.core.groupby import groupby
|
|
2
7
|
|
|
3
8
|
|
|
4
|
-
def _nest_items(
|
|
9
|
+
def _nest_items(
|
|
10
|
+
nested_items: MutableMapping[Any, list[Any]],
|
|
11
|
+
item: Any,
|
|
12
|
+
id_key: Any,
|
|
13
|
+
children_key: Any,
|
|
14
|
+
) -> None:
|
|
5
15
|
children_items = nested_items.pop(item[id_key], [])
|
|
6
16
|
item[children_key] = children_items
|
|
7
17
|
for child_item in children_items:
|
|
8
18
|
_nest_items(nested_items, child_item, id_key, children_key)
|
|
9
19
|
|
|
10
20
|
|
|
11
|
-
def nest(
|
|
21
|
+
def nest(
|
|
22
|
+
items: Sequence[Mapping[Any, Any]],
|
|
23
|
+
id_key: Any,
|
|
24
|
+
parent_id_key: Any,
|
|
25
|
+
children_key: Any,
|
|
26
|
+
) -> list[Any] | None:
|
|
12
27
|
if any(
|
|
13
28
|
[id_key == parent_id_key, id_key == children_key, parent_id_key == children_key]
|
|
14
29
|
):
|
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
from collections.abc import MutableMapping
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
1
4
|
from benedict.utils import type_util
|
|
2
5
|
|
|
3
6
|
|
|
4
|
-
def remove(d, keys, *args):
|
|
7
|
+
def remove(d: MutableMapping[Any, Any], keys: Any, *args: Any) -> None:
|
|
5
8
|
if type_util.is_string(keys):
|
|
6
9
|
keys = [keys]
|
|
7
10
|
keys += args
|
|
@@ -1,26 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, MutableMapping, Sequence
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
1
6
|
from benedict.core.traverse import traverse
|
|
2
7
|
from benedict.utils import type_util
|
|
3
8
|
|
|
9
|
+
_K = TypeVar("_K")
|
|
10
|
+
_V = TypeVar("_V")
|
|
11
|
+
|
|
4
12
|
|
|
5
|
-
def _get_term(value, case_sensitive):
|
|
13
|
+
def _get_term(value: Any, case_sensitive: bool) -> tuple[Any, bool]:
|
|
6
14
|
v_is_str = type_util.is_string(value)
|
|
7
15
|
v = value.lower() if (v_is_str and not case_sensitive) else value
|
|
8
16
|
return (v, v_is_str)
|
|
9
17
|
|
|
10
18
|
|
|
11
|
-
def _get_match(query, value, exact, case_sensitive):
|
|
19
|
+
def _get_match(query: Any, value: Any, exact: bool, case_sensitive: bool) -> bool:
|
|
12
20
|
q, q_is_str = _get_term(query, case_sensitive)
|
|
13
21
|
v, v_is_str = _get_term(value, case_sensitive)
|
|
14
22
|
# TODO: add regex support
|
|
15
23
|
if q_is_str and v_is_str and not exact:
|
|
16
24
|
return q in v
|
|
17
|
-
return q == v
|
|
25
|
+
return bool(q == v)
|
|
18
26
|
|
|
19
27
|
|
|
20
|
-
def search(
|
|
28
|
+
def search(
|
|
29
|
+
d: MutableMapping[_K, _V],
|
|
30
|
+
query: Any,
|
|
31
|
+
in_keys: bool = True,
|
|
32
|
+
in_values: bool = True,
|
|
33
|
+
exact: bool = False,
|
|
34
|
+
case_sensitive: bool = True,
|
|
35
|
+
) -> list[tuple[Any, Any, Any]]:
|
|
21
36
|
items = []
|
|
22
37
|
|
|
23
|
-
def _search_item(
|
|
38
|
+
def _search_item(
|
|
39
|
+
item_dict: Mapping[_K, _V] | Sequence[Any] | None,
|
|
40
|
+
item_key: _K | int,
|
|
41
|
+
item_value: _V | None,
|
|
42
|
+
) -> None:
|
|
24
43
|
match_key = in_keys and _get_match(query, item_key, exact, case_sensitive)
|
|
25
44
|
match_val = in_values and _get_match(query, item_value, exact, case_sensitive)
|
|
26
45
|
if any([match_key, match_val]):
|