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.
Files changed (90) hide show
  1. {python_benedict-0.34.0/python_benedict.egg-info → python_benedict-0.35.0}/PKG-INFO +12 -8
  2. {python_benedict-0.34.0 → python_benedict-0.35.0}/README.md +4 -4
  3. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/clean.py +28 -10
  4. python_benedict-0.35.0/benedict/core/clone.py +18 -0
  5. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/dump.py +3 -1
  6. python_benedict-0.35.0/benedict/core/filter.py +21 -0
  7. python_benedict-0.35.0/benedict/core/find.py +16 -0
  8. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/flatten.py +13 -6
  9. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/groupby.py +10 -2
  10. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/invert.py +12 -4
  11. python_benedict-0.35.0/benedict/core/items_sorted.py +25 -0
  12. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/keylists.py +17 -4
  13. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/keypaths.py +13 -2
  14. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/match.py +10 -1
  15. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/merge.py +24 -5
  16. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/move.py +9 -1
  17. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/nest.py +17 -2
  18. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/remove.py +4 -1
  19. python_benedict-0.35.0/benedict/core/rename.py +10 -0
  20. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/search.py +24 -5
  21. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/standardize.py +5 -3
  22. python_benedict-0.35.0/benedict/core/subset.py +24 -0
  23. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/swap.py +7 -1
  24. python_benedict-0.35.0/benedict/core/traverse.py +46 -0
  25. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/unflatten.py +8 -3
  26. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/unique.py +5 -1
  27. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/__init__.py +100 -62
  28. python_benedict-0.35.0/benedict/dicts/base/base_dict.py +178 -0
  29. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/io/io_dict.py +75 -47
  30. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/io/io_util.py +44 -23
  31. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keyattr/keyattr_dict.py +30 -18
  32. python_benedict-0.35.0/benedict/dicts/keylist/keylist_dict.py +116 -0
  33. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keylist/keylist_util.py +21 -9
  34. python_benedict-0.35.0/benedict/dicts/keypath/keypath_dict.py +77 -0
  35. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keypath/keypath_util.py +12 -8
  36. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/parse/parse_dict.py +200 -60
  37. python_benedict-0.35.0/benedict/dicts/parse/parse_util.py +344 -0
  38. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/exceptions.py +1 -1
  39. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/extras.py +8 -8
  40. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/metadata.py +1 -1
  41. python_benedict-0.35.0/benedict/py.typed +0 -0
  42. python_benedict-0.35.0/benedict/serializers/__init__.py +194 -0
  43. python_benedict-0.35.0/benedict/serializers/abstract.py +27 -0
  44. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/base64.py +23 -13
  45. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/cli.py +13 -8
  46. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/csv.py +8 -5
  47. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/html.py +8 -4
  48. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/ini.py +20 -9
  49. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/json.py +10 -7
  50. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/pickle.py +10 -4
  51. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/plist.py +5 -4
  52. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/query_string.py +20 -6
  53. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/toml.py +6 -4
  54. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/xls.py +21 -13
  55. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/xml.py +9 -5
  56. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/serializers/yaml.py +9 -7
  57. python_benedict-0.35.0/benedict/utils/type_util.py +109 -0
  58. {python_benedict-0.34.0 → python_benedict-0.35.0}/pyproject.toml +29 -3
  59. {python_benedict-0.34.0 → python_benedict-0.35.0/python_benedict.egg-info}/PKG-INFO +12 -8
  60. {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/SOURCES.txt +1 -0
  61. {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/requires.txt +4 -2
  62. python_benedict-0.34.0/benedict/core/clone.py +0 -8
  63. python_benedict-0.34.0/benedict/core/filter.py +0 -13
  64. python_benedict-0.34.0/benedict/core/find.py +0 -5
  65. python_benedict-0.34.0/benedict/core/items_sorted.py +0 -10
  66. python_benedict-0.34.0/benedict/core/rename.py +0 -5
  67. python_benedict-0.34.0/benedict/core/subset.py +0 -12
  68. python_benedict-0.34.0/benedict/core/traverse.py +0 -29
  69. python_benedict-0.34.0/benedict/dicts/base/base_dict.py +0 -163
  70. python_benedict-0.34.0/benedict/dicts/keylist/keylist_dict.py +0 -95
  71. python_benedict-0.34.0/benedict/dicts/keypath/keypath_dict.py +0 -65
  72. python_benedict-0.34.0/benedict/dicts/parse/parse_util.py +0 -231
  73. python_benedict-0.34.0/benedict/serializers/__init__.py +0 -88
  74. python_benedict-0.34.0/benedict/serializers/abstract.py +0 -17
  75. python_benedict-0.34.0/benedict/utils/type_util.py +0 -95
  76. {python_benedict-0.34.0 → python_benedict-0.35.0}/LICENSE.txt +0 -0
  77. {python_benedict-0.34.0 → python_benedict-0.35.0}/MANIFEST.in +0 -0
  78. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/__init__.py +0 -0
  79. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/core/__init__.py +0 -0
  80. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/base/__init__.py +0 -0
  81. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/io/__init__.py +0 -0
  82. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keyattr/__init__.py +0 -0
  83. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keylist/__init__.py +0 -0
  84. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/keypath/__init__.py +0 -0
  85. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/dicts/parse/__init__.py +0 -0
  86. {python_benedict-0.34.0 → python_benedict-0.35.0}/benedict/utils/__init__.py +0 -0
  87. {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/dependency_links.txt +0 -0
  88. {python_benedict-0.34.0 → python_benedict-0.35.0}/python_benedict.egg-info/top_level.txt +0 -0
  89. {python_benedict-0.34.0 → python_benedict-0.35.0}/setup.cfg +0 -0
  90. {python_benedict-0.34.0 → python_benedict-0.35.0}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: python-benedict
3
- Version: 0.34.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<9.0.0,>=8.12.0; extra == "parse"
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<1.0.0,>=0.12.0; extra == "xml"
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://img.shields.io/pypi/pyversions/python-benedict.svg?color=blue&logo=python&logoColor=white)](https://www.python.org/)
89
93
  [![](https://img.shields.io/pypi/v/python-benedict.svg?color=blue&logo=pypi&logoColor=white)](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/badge/github/fabiocaccamo/python-benedict/main.svg)](https://results.pre-commit.ci/latest/github/fabiocaccamo/python-benedict/main)
95
99
  [![](https://img.shields.io/github/actions/workflow/status/fabiocaccamo/python-benedict/test-package.yml?branch=main&label=build&logo=github)](https://github.com/fabiocaccamo/python-benedict)
96
100
  [![](https://img.shields.io/codecov/c/gh/fabiocaccamo/python-benedict?logo=codecov)](https://codecov.io/gh/fabiocaccamo/python-benedict)
97
- [![](https://img.shields.io/codeclimate/maintainability/fabiocaccamo/python-benedict?logo=code-climate)](https://codeclimate.com/github/fabiocaccamo/python-benedict/)
98
101
  [![](https://img.shields.io/codacy/grade/0dbd5cc2089f4dce80a0e49e6822be3c?logo=codacy)](https://www.codacy.com/app/fabiocaccamo/python-benedict)
99
102
  [![](https://img.shields.io/scrutinizer/quality/g/fabiocaccamo/python-benedict?logo=scrutinizer)](https://scrutinizer-ci.com/g/fabiocaccamo/python-benedict/?branch=main)
100
103
  [![](https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=black)](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 (or a tuple) of keys**.
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
- k = d.keypaths(indexes=False)
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 [Twitter](https://twitter.com/fabiocaccamo)
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/badge/github/fabiocaccamo/python-benedict/main.svg)](https://results.pre-commit.ci/latest/github/fabiocaccamo/python-benedict/main)
8
8
  [![](https://img.shields.io/github/actions/workflow/status/fabiocaccamo/python-benedict/test-package.yml?branch=main&label=build&logo=github)](https://github.com/fabiocaccamo/python-benedict)
9
9
  [![](https://img.shields.io/codecov/c/gh/fabiocaccamo/python-benedict?logo=codecov)](https://codecov.io/gh/fabiocaccamo/python-benedict)
10
- [![](https://img.shields.io/codeclimate/maintainability/fabiocaccamo/python-benedict?logo=code-climate)](https://codeclimate.com/github/fabiocaccamo/python-benedict/)
11
10
  [![](https://img.shields.io/codacy/grade/0dbd5cc2089f4dce80a0e49e6822be3c?logo=codacy)](https://www.codacy.com/app/fabiocaccamo/python-benedict)
12
11
  [![](https://img.shields.io/scrutinizer/quality/g/fabiocaccamo/python-benedict?logo=scrutinizer)](https://scrutinizer-ci.com/g/fabiocaccamo/python-benedict/?branch=main)
13
12
  [![](https://img.shields.io/badge/code%20style-black-000000.svg?logo=python&logoColor=black)](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 (or a tuple) of keys**.
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
- k = d.keypaths(indexes=False)
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 [Twitter](https://twitter.com/fabiocaccamo)
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
- def _clean_dict(d, strings, collections):
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(ls, strings, collections):
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(values, strings, collections):
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, list) and collections:
57
+ elif isinstance(value, MutableSequence) and collections:
42
58
  value = _clean_list(value, strings=strings, collections=collections) or None
43
- elif isinstance(value, dict) and collections:
44
- value = _clean_dict(value, strings=strings, collections=collections) or None
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
@@ -1,7 +1,9 @@
1
+ from typing import Any
2
+
1
3
  from benedict.serializers import JSONSerializer
2
4
 
3
5
 
4
- def dump(obj, **kwargs):
6
+ def dump(obj: Any, **kwargs: Any) -> str:
5
7
  serializer = JSONSerializer()
6
8
  options = {"indent": 4, "sort_keys": True}
7
9
  options.update(**kwargs)
@@ -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 benedict.core import clone
2
- from benedict.utils import type_util
1
+ from collections.abc import Mapping
2
+ from typing import Any
3
3
 
4
+ from benedict.core.clone import clone
4
5
 
5
- def _flatten_key(base_key, key, separator):
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(d, base_dict, base_key, separator):
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 type_util.is_dict(value):
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 benedict.core import clone
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(d, key, value, flat):
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(d, parent_keys, indexes):
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(ls, parent_keys, indexes):
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(value, parent_keys, indexes):
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(d, indexes=False):
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(d, separator=".", indexes=False):
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
- kps.sort()
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(d, pattern, separator=".", indexes=True):
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(d, other, overwrite=True, concat=False):
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(d, key, value, overwrite=True, concat=False):
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, None)
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(d, other, *args, **kwargs):
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
- def move(d, key_src, key_dest, overwrite=True):
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(nested_items, item, id_key, children_key):
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(items, id_key, parent_id_key, children_key):
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
@@ -0,0 +1,10 @@
1
+ from collections.abc import MutableMapping
2
+ from typing import Any, TypeVar
3
+
4
+ from benedict.core.move import move
5
+
6
+ _K = TypeVar("_K")
7
+
8
+
9
+ def rename(d: MutableMapping[_K, Any], key: _K, key_new: _K) -> None:
10
+ move(d, key, key_new, overwrite=False)
@@ -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(d, query, in_keys=True, in_values=True, exact=False, case_sensitive=True):
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(item_dict, item_key, item_value):
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]):