mappingtools 0.0.2__tar.gz → 0.0.4__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: mappingtools
3
- Version: 0.0.2
3
+ Version: 0.0.4
4
4
  Summary: MappingTools. Do stuff with Mappings
5
5
  Project-URL: Homepage, https://erivlis.github.io/mappingtools
6
6
  Project-URL: Bug Tracker, https://github.com/erivlis/mappingtools/issues
@@ -16,15 +16,13 @@ Classifier: Natural Language :: English
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python :: 3
18
18
  Classifier: Programming Language :: Python :: 3 :: Only
19
- Classifier: Programming Language :: Python :: 3.8
20
- Classifier: Programming Language :: Python :: 3.9
21
19
  Classifier: Programming Language :: Python :: 3.10
22
20
  Classifier: Programming Language :: Python :: 3.11
23
21
  Classifier: Programming Language :: Python :: 3.12
24
22
  Classifier: Programming Language :: Python :: Implementation :: CPython
25
23
  Classifier: Topic :: Software Development :: Libraries
26
24
  Classifier: Typing :: Typed
27
- Requires-Python: >=3.8
25
+ Requires-Python: >=3.10
28
26
  Provides-Extra: dev
29
27
  Requires-Dist: ruff; extra == 'dev'
30
28
  Provides-Extra: docs
@@ -241,6 +239,23 @@ print(unwrapped_data)
241
239
  # Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]
242
240
  ```
243
241
 
242
+ ### `MappingCollector`
243
+
244
+ A class designed to collect key-value pairs into an internal mapping,
245
+ with two modes of operation: one_to_one and one_to_many.
246
+ The mode determines whether each key maps to a single value or multiple values.
247
+
248
+ ```python
249
+ from mappingtools import MappingCollector, MappingCollectorMode
250
+
251
+ collector = MappingCollector(MappingCollectorMode.one_to_many)
252
+ collector.add('a', 1)
253
+ collector.add('a', 2)
254
+ collector.collect([('b', 3), ('b', 4)])
255
+ print(collector.mapping)
256
+ # Output: {'a': [1, 2], 'b': [3, 4]}
257
+ ```
258
+
244
259
  ## Development
245
260
 
246
261
  ### Ruff
@@ -254,11 +269,11 @@ ruff check src
254
269
  #### Standard (cobertura) XML Coverage Report
255
270
 
256
271
  ```shell
257
- python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=xml --junitxml=test_results.xml
272
+ python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=xml
258
273
  ```
259
274
 
260
275
  #### HTML Coverage Report
261
276
 
262
277
  ```shell
263
- python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html --junitxml=test_results.xml
278
+ python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html
264
279
  ```
@@ -197,6 +197,23 @@ print(unwrapped_data)
197
197
  # Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]
198
198
  ```
199
199
 
200
+ ### `MappingCollector`
201
+
202
+ A class designed to collect key-value pairs into an internal mapping,
203
+ with two modes of operation: one_to_one and one_to_many.
204
+ The mode determines whether each key maps to a single value or multiple values.
205
+
206
+ ```python
207
+ from mappingtools import MappingCollector, MappingCollectorMode
208
+
209
+ collector = MappingCollector(MappingCollectorMode.one_to_many)
210
+ collector.add('a', 1)
211
+ collector.add('a', 2)
212
+ collector.collect([('b', 3), ('b', 4)])
213
+ print(collector.mapping)
214
+ # Output: {'a': [1, 2], 'b': [3, 4]}
215
+ ```
216
+
200
217
  ## Development
201
218
 
202
219
  ### Ruff
@@ -210,11 +227,11 @@ ruff check src
210
227
  #### Standard (cobertura) XML Coverage Report
211
228
 
212
229
  ```shell
213
- python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=xml --junitxml=test_results.xml
230
+ python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=xml
214
231
  ```
215
232
 
216
233
  #### HTML Coverage Report
217
234
 
218
235
  ```shell
219
- python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html --junitxml=test_results.xml
236
+ python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html
220
237
  ```
@@ -1,19 +1,17 @@
1
1
  [project]
2
2
  name = "mappingtools"
3
- version = "0.0.2"
3
+ version = "0.0.4"
4
4
  authors = [
5
5
  { name = "Eran Rivlis", email = "eran@rivlis.info" },
6
6
  ]
7
7
  description = "MappingTools. Do stuff with Mappings"
8
8
  readme = "README.md"
9
- requires-python = ">=3.8"
9
+ requires-python = ">=3.10"
10
10
  classifiers = [
11
11
  "Development Status :: 4 - Beta",
12
12
  "Intended Audience :: Developers",
13
13
  "Intended Audience :: Information Technology",
14
14
  "Programming Language :: Python :: 3",
15
- "Programming Language :: Python :: 3.8",
16
- "Programming Language :: Python :: 3.9",
17
15
  "Programming Language :: Python :: 3.10",
18
16
  "Programming Language :: Python :: 3.11",
19
17
  "Programming Language :: Python :: 3.12",
@@ -2,13 +2,104 @@ import dataclasses
2
2
  import inspect
3
3
  from collections import defaultdict
4
4
  from collections.abc import Callable, Generator, Iterable, Mapping
5
+ from enum import Enum, auto
5
6
  from itertools import chain
6
7
  from typing import Any, TypeVar
7
8
 
8
- K = TypeVar("K")
9
+ K = TypeVar('K')
10
+ KT = TypeVar('KT')
11
+ VT = TypeVar('VT')
12
+ VT_co = TypeVar('VT_co')
13
+
14
+
15
+ class MappingCollectorMode(Enum):
16
+ """
17
+ Define an enumeration class for mapping collector modes with two options: one_to_one and one_to_many.
18
+ """
19
+ one_to_one = auto()
20
+ one_to_many = auto()
21
+
22
+
23
+ class MappingCollector:
24
+
25
+ def __init__(self, mode: MappingCollectorMode = MappingCollectorMode.one_to_one, *args, **kwargs):
26
+ """
27
+ Initialize the MappingCollector with the specified mode.
28
+
29
+ Args:
30
+ mode (MappingCollectorMode): The mode for collecting mappings.
31
+ *args: Variable positional arguments used to initialize the internal mapping.
32
+ **kwargs: Variable keyword arguments used to initialize the internal mapping.
33
+ """
34
+
35
+ self.mode = mode
36
+
37
+ match self.mode:
38
+ case MappingCollectorMode.one_to_one:
39
+ self._mapping = dict(*args, **kwargs)
40
+ case MappingCollectorMode.one_to_many:
41
+ self._mapping = defaultdict(list, *args, **kwargs)
42
+ case _:
43
+ raise ValueError("Invalid mode")
44
+
45
+ def __repr__(self):
46
+ return f'MappingCollector(mode={self.mode}, mapping={self.mapping})'
47
+
48
+ @property
49
+ def mapping(self) -> Mapping[KT, VT_co]:
50
+ """
51
+ Return a shallow copy of the internal mapping.
52
+
53
+ Returns:
54
+ Mapping[KT, VT_co]: A shallow copy of the internal mapping.
55
+ """
56
+ return dict(self._mapping)
57
+
58
+ def add(self, key: KT, value: VT):
59
+ """
60
+ Add a key-value pair to the internal mapping based on the specified mode.
61
+
62
+ Args:
63
+ key: The key to be added to the mapping.
64
+ value: The value corresponding to the key.
65
+
66
+ Returns:
67
+ None
68
+ """
69
+ match self.mode:
70
+ case MappingCollectorMode.one_to_one:
71
+ self._mapping[key] = value
72
+ case MappingCollectorMode.one_to_many:
73
+ self._mapping[key].append(value)
74
+
75
+ def collect(self, iterable: Iterable[tuple[KT, VT]]):
76
+ """
77
+ Collect key-value pairs from the given iterable and add them to the internal mapping
78
+ based on the specified mode.
79
+
80
+ Args:
81
+ iterable (Iterable[tuple[KT, VT]]): An iterable containing key-value pairs to collect.
82
+
83
+ Returns:
84
+ None
85
+ """
86
+ for k, v in iterable:
87
+ self.add(k, v)
9
88
 
10
89
 
11
90
  def _take(keys: Iterable[K], mapping: Mapping[K, Any], exclude: bool = False) -> dict[K, Any]:
91
+ """
92
+ Return a dictionary pertaining to the specified keys and their corresponding values from the mapping.
93
+
94
+ Args:
95
+ keys (Iterable[K]): The keys to include in the resulting dictionary.
96
+ mapping (Mapping[K, Any]): The mapping to extract key-value pairs from.
97
+ exclude (bool, optional): If True, exclude the specified keys from the mapping. Defaults to False.
98
+
99
+ Returns:
100
+ dict[K, Any]: A dictionary with the selected keys and their values from the mapping.
101
+ """
102
+
12
103
  if not isinstance(mapping, Mapping):
13
104
  raise TypeError(f"Parameter 'mapping' should be of type 'Mapping', but instead is type '{type(mapping)}'")
14
105
 
@@ -109,7 +200,7 @@ def _process_obj(obj: Any,
109
200
  return obj
110
201
 
111
202
 
112
- def dictify(obj, key_converter: Callable[[Any], str] | None = None):
203
+ def dictify(obj: Any, key_converter: Callable[[Any], str] | None = None) -> Any:
113
204
  """Dictify an object using a specified key converter.
114
205
 
115
206
  Args:
@@ -192,4 +283,5 @@ def _unwrap_class(obj):
192
283
  return [{'key': k, 'value': unwrap(v)} for k, v in inspect.getmembers(obj) if not k.startswith('_')]
193
284
 
194
285
 
195
- __all__ = ('dictify', 'distinct', 'keep', 'inverse', 'nested_defaultdict', 'remove', 'unwrap')
286
+ __all__ = ('dictify', 'distinct', 'keep', 'inverse', 'nested_defaultdict', 'remove', 'unwrap', 'MappingCollector',
287
+ 'MappingCollectorMode')
@@ -1,9 +1,8 @@
1
+ # Generated by CodiumAI
1
2
  import dataclasses
2
3
 
3
4
  from mappingtools import dictify
4
5
 
5
- # Generated by CodiumAI
6
-
7
6
 
8
7
  class TestDictify:
9
8
 
@@ -1,5 +1,4 @@
1
1
  # Generated by CodiumAI
2
-
3
2
  from mappingtools import distinct
4
3
 
5
4
 
@@ -1,5 +1,4 @@
1
1
  # Generated by CodiumAI
2
-
3
2
  from mappingtools import inverse
4
3
 
5
4
 
@@ -0,0 +1,155 @@
1
+ # Generated by CodiumAI
2
+ import pytest
3
+ from mappingtools import MappingCollector, MappingCollectorMode
4
+
5
+
6
+ class TestMappingCollector:
7
+
8
+ # Initialize MappingCollector with default mode and verify internal mapping is a dictionary
9
+ def test_initialize_default_mode(self):
10
+ # Arrange
11
+ collector = MappingCollector()
12
+
13
+ # Act
14
+ collector.add(1, 2)
15
+ collector.add(1, 3)
16
+ result = collector.mapping
17
+ item0 = result[1]
18
+
19
+ # Assert
20
+ assert isinstance(result, dict)
21
+ assert isinstance(item0, int)
22
+ assert item0 != 2
23
+ assert item0 == 3
24
+
25
+ # Initialize MappingCollector with one_to_many mode and verify internal mapping is a defaultdict
26
+ def test_initialize_one_to_many_mode(self):
27
+ # Arrange
28
+ collector = MappingCollector(MappingCollectorMode.one_to_many)
29
+
30
+ # Act
31
+ collector.add(1, 2)
32
+ collector.add(1, 3)
33
+ result = collector.mapping
34
+ item0 = result[1]
35
+
36
+ # Assert
37
+ assert isinstance(result, dict)
38
+ assert isinstance(item0, list)
39
+ assert item0 == [2, 3]
40
+
41
+ # Add a single key-value pair in one_to_one mode and verify the mapping
42
+ def test_add_single_key_value_one_to_one(self):
43
+ # Arrange
44
+ collector = MappingCollector()
45
+
46
+ # Act
47
+ collector.add('key1', 'value1')
48
+ result = collector.mapping
49
+
50
+ # Assert
51
+ assert result == {'key1': 'value1'}
52
+
53
+ # Add multiple key-value pairs in one_to_many mode and verify the mapping
54
+ def test_add_multiple_key_values_one_to_many(self):
55
+ # Arrange
56
+ collector = MappingCollector(MappingCollectorMode.one_to_many)
57
+
58
+ # Act
59
+ collector.add('key1', 'value1')
60
+ collector.add('key1', 'value2')
61
+ result = collector.mapping
62
+
63
+ # Assert
64
+ assert result == {'key1': ['value1', 'value2']}
65
+
66
+ # Collect key-value pairs from an iterable in one_to_one mode and verify the mapping
67
+ def test_collect_iterable_one_to_one(self):
68
+ # Arrange
69
+ collector = MappingCollector()
70
+ iterable = [('key1', 'value1'), ('key2', 'value2')]
71
+
72
+ # Act
73
+ collector.collect(iterable)
74
+ result = collector.mapping
75
+
76
+ # Assert
77
+ assert result == {'key1': 'value1', 'key2': 'value2'}
78
+
79
+ # Collect key-value pairs from an iterable in one_to_many mode and verify the mapping
80
+ def test_collect_iterable_one_to_many(self):
81
+ # Arrange
82
+ collector = MappingCollector(MappingCollectorMode.one_to_many)
83
+ iterable = [('key1', 'value1'), ('key1', 'value2')]
84
+
85
+ # Act
86
+ collector.collect(iterable)
87
+ result = collector.mapping
88
+
89
+ # Assert
90
+ assert result == {'key1': ['value1', 'value2']}
91
+
92
+ # Initialize MappingCollector with an invalid mode and verify it raises an error
93
+ def test_initialize_invalid_mode(self):
94
+ # Arrange / Act / Assert
95
+ with pytest.raises(ValueError): # noqa: PT011
96
+ MappingCollector(mode="invalid_mode")
97
+
98
+ # Add a key-value pair with a non-hashable key and verify it raises an error
99
+ def test_add_non_hashable_key(self):
100
+ # Arrange
101
+ collector = MappingCollector()
102
+
103
+ # Act / Assert
104
+ with pytest.raises(TypeError):
105
+ collector.add(['non-hashable'], 'value')
106
+
107
+ # Collect key-value pairs from an empty iterable and verify the internal mapping remains unchanged
108
+ def test_collect_empty_iterable(self):
109
+ # Arrange
110
+ collector = MappingCollector()
111
+
112
+ # Act
113
+ collector.collect([])
114
+ result = collector.mapping
115
+
116
+ # Assert
117
+ assert result == {}
118
+
119
+ # Add a key-value pair with None as the key and verify the mapping
120
+ def test_add_none_key(self):
121
+ # Arrange
122
+ collector = MappingCollector()
123
+
124
+ # Act
125
+ collector.add(None, 'value')
126
+ result = collector.mapping
127
+
128
+ # Assert
129
+ assert result == {None: 'value'}
130
+
131
+ # Collect key-value pairs with duplicate keys in one_to_one mode and verify the last value is retained
132
+ def test_collect_duplicate_keys_one_to_one(self):
133
+ # Arrange
134
+ collector = MappingCollector()
135
+ iterable = [('key1', 'value1'), ('key1', 'value2')]
136
+
137
+ # Act
138
+ collector.collect(iterable)
139
+ result = collector.mapping
140
+
141
+ # Assert
142
+ assert result == {'key1': 'value2'}
143
+
144
+ # Collect key-value pairs with duplicate keys in one_to_many mode and verify all values are appended
145
+ def test_collect_duplicate_keys_one_to_many(self):
146
+ # Arrange
147
+ collector = MappingCollector(MappingCollectorMode.one_to_many)
148
+ iterable = [('key1', 'value1'), ('key1', 'value2')]
149
+
150
+ # Act
151
+ collector.collect(iterable)
152
+ result = collector.mapping
153
+
154
+ # Assert
155
+ assert result == {'key1': ['value1', 'value2']}
@@ -1,5 +1,4 @@
1
1
  # Generated by CodiumAI
2
-
3
2
  from mappingtools import nested_defaultdict
4
3
 
5
4
 
@@ -1,5 +1,4 @@
1
1
  # Generated by CodiumAI
2
-
3
2
  from mappingtools import remove
4
3
 
5
4
 
File without changes
File without changes
File without changes