python-benedict 0.34.1__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.1/python_benedict.egg-info → python_benedict-0.35.0}/PKG-INFO +9 -6
- {python_benedict-0.34.1 → python_benedict-0.35.0}/README.md +1 -2
- {python_benedict-0.34.1 → 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.1 → 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.1 → python_benedict-0.35.0}/benedict/core/flatten.py +13 -6
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/groupby.py +10 -2
- {python_benedict-0.34.1 → 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.1 → python_benedict-0.35.0}/benedict/core/keylists.py +17 -4
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/keypaths.py +11 -1
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/match.py +10 -1
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/merge.py +24 -5
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/move.py +9 -1
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/nest.py +17 -2
- {python_benedict-0.34.1 → 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.1 → python_benedict-0.35.0}/benedict/core/search.py +24 -5
- {python_benedict-0.34.1 → 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.1 → 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.1 → python_benedict-0.35.0}/benedict/core/unflatten.py +8 -3
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/unique.py +5 -1
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/__init__.py +97 -61
- python_benedict-0.35.0/benedict/dicts/base/base_dict.py +178 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/io/io_dict.py +75 -47
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/io/io_util.py +44 -23
- {python_benedict-0.34.1 → 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.1 → 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.1 → python_benedict-0.35.0}/benedict/dicts/keypath/keypath_util.py +12 -8
- {python_benedict-0.34.1 → 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.1 → python_benedict-0.35.0}/benedict/exceptions.py +1 -1
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/extras.py +8 -8
- {python_benedict-0.34.1 → 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.1 → python_benedict-0.35.0}/benedict/serializers/base64.py +23 -13
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/cli.py +13 -8
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/csv.py +8 -5
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/html.py +8 -4
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/ini.py +20 -9
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/json.py +10 -7
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/pickle.py +10 -4
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/plist.py +5 -4
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/query_string.py +20 -6
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/toml.py +6 -4
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/xls.py +21 -13
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/serializers/xml.py +9 -5
- {python_benedict-0.34.1 → 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.1 → python_benedict-0.35.0}/pyproject.toml +29 -3
- {python_benedict-0.34.1 → python_benedict-0.35.0/python_benedict.egg-info}/PKG-INFO +9 -6
- {python_benedict-0.34.1 → python_benedict-0.35.0}/python_benedict.egg-info/SOURCES.txt +1 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/python_benedict.egg-info/requires.txt +4 -2
- python_benedict-0.34.1/benedict/core/clone.py +0 -8
- python_benedict-0.34.1/benedict/core/filter.py +0 -13
- python_benedict-0.34.1/benedict/core/find.py +0 -5
- python_benedict-0.34.1/benedict/core/items_sorted.py +0 -10
- python_benedict-0.34.1/benedict/core/rename.py +0 -5
- python_benedict-0.34.1/benedict/core/subset.py +0 -12
- python_benedict-0.34.1/benedict/core/traverse.py +0 -29
- python_benedict-0.34.1/benedict/dicts/base/base_dict.py +0 -163
- python_benedict-0.34.1/benedict/dicts/keylist/keylist_dict.py +0 -95
- python_benedict-0.34.1/benedict/dicts/keypath/keypath_dict.py +0 -65
- python_benedict-0.34.1/benedict/dicts/parse/parse_util.py +0 -231
- python_benedict-0.34.1/benedict/serializers/__init__.py +0 -88
- python_benedict-0.34.1/benedict/serializers/abstract.py +0 -17
- python_benedict-0.34.1/benedict/utils/type_util.py +0 -95
- {python_benedict-0.34.1 → python_benedict-0.35.0}/LICENSE.txt +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/MANIFEST.in +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/core/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/base/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/io/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/keyattr/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/keylist/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/keypath/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/dicts/parse/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/benedict/utils/__init__.py +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/python_benedict.egg-info/dependency_links.txt +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/python_benedict.egg-info/top_level.txt +0 -0
- {python_benedict-0.34.1 → python_benedict-0.35.0}/setup.cfg +0 -0
- {python_benedict-0.34.1 → 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)
|
|
@@ -1127,7 +1130,7 @@ Released under [MIT License](LICENSE.txt).
|
|
|
1127
1130
|
|
|
1128
1131
|
- :star: Star this project on [GitHub](https://github.com/fabiocaccamo/python-benedict)
|
|
1129
1132
|
- :octocat: Follow me on [GitHub](https://github.com/fabiocaccamo)
|
|
1130
|
-
- :blue_heart: Follow me on [
|
|
1133
|
+
- :blue_heart: Follow me on [Bluesky](https://bsky.app/profile/fabiocaccamo.bsky.social)
|
|
1131
1134
|
- :moneybag: Sponsor me on [Github](https://github.com/sponsors/fabiocaccamo)
|
|
1132
1135
|
|
|
1133
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)
|
|
@@ -1040,7 +1039,7 @@ Released under [MIT License](LICENSE.txt).
|
|
|
1040
1039
|
|
|
1041
1040
|
- :star: Star this project on [GitHub](https://github.com/fabiocaccamo/python-benedict)
|
|
1042
1041
|
- :octocat: Follow me on [GitHub](https://github.com/fabiocaccamo)
|
|
1043
|
-
- :blue_heart: Follow me on [
|
|
1042
|
+
- :blue_heart: Follow me on [Bluesky](https://bsky.app/profile/fabiocaccamo.bsky.social)
|
|
1044
1043
|
- :moneybag: Sponsor me on [Github](https://github.com/sponsors/fabiocaccamo)
|
|
1045
1044
|
|
|
1046
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,8 +1,18 @@
|
|
|
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.")
|
|
@@ -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]):
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import re
|
|
2
|
+
from collections.abc import MutableMapping
|
|
3
|
+
from typing import Any
|
|
2
4
|
|
|
3
5
|
from slugify import slugify
|
|
4
6
|
|
|
@@ -7,7 +9,7 @@ from benedict.core.traverse import traverse
|
|
|
7
9
|
from benedict.utils import type_util
|
|
8
10
|
|
|
9
11
|
|
|
10
|
-
def _standardize_item(d, key, value):
|
|
12
|
+
def _standardize_item(d: MutableMapping[Any, Any], key: Any, value: Any) -> None:
|
|
11
13
|
if type_util.is_string(key):
|
|
12
14
|
# https://stackoverflow.com/a/12867228/2096218
|
|
13
15
|
norm_key = re.sub(r"((?<=[a-z0-9])[A-Z]|(?!^)[A-Z](?=[a-z]))", r"_\1", key)
|
|
@@ -15,5 +17,5 @@ def _standardize_item(d, key, value):
|
|
|
15
17
|
rename(d, key, norm_key)
|
|
16
18
|
|
|
17
19
|
|
|
18
|
-
def standardize(d):
|
|
19
|
-
traverse(d, _standardize_item)
|
|
20
|
+
def standardize(d: MutableMapping[Any, Any]) -> None:
|
|
21
|
+
traverse(d, _standardize_item) # type: ignore[arg-type]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from collections.abc import MutableMapping
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from benedict.core.clone import clone
|
|
7
|
+
from benedict.utils import type_util
|
|
8
|
+
|
|
9
|
+
_K = TypeVar("_K")
|
|
10
|
+
_V = TypeVar("_V")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def subset(
|
|
14
|
+
d: MutableMapping[_K, _V], keys: _K | list[_K], *args: Any
|
|
15
|
+
) -> MutableMapping[_K, _V | None]:
|
|
16
|
+
new_dict: MutableMapping[_K, _V | None] = clone(d, empty=True) # type: ignore[arg-type]
|
|
17
|
+
if type_util.is_string(keys):
|
|
18
|
+
key_list: list[_K] = [keys]
|
|
19
|
+
else:
|
|
20
|
+
key_list = list(keys) if isinstance(keys, list) else [keys]
|
|
21
|
+
key_list.extend(args)
|
|
22
|
+
for key in key_list:
|
|
23
|
+
new_dict[key] = d.get(key)
|
|
24
|
+
return new_dict
|