mappingtools 0.2.2__tar.gz → 0.3.1__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 (37) hide show
  1. {mappingtools-0.2.2 → mappingtools-0.3.1}/.github/workflows/test-beta.yml +1 -1
  2. {mappingtools-0.2.2 → mappingtools-0.3.1}/.gitignore +2 -0
  3. {mappingtools-0.2.2 → mappingtools-0.3.1}/PKG-INFO +176 -136
  4. {mappingtools-0.2.2 → mappingtools-0.3.1}/README.md +173 -133
  5. {mappingtools-0.2.2 → mappingtools-0.3.1}/pyproject.toml +4 -4
  6. mappingtools-0.3.1/src/mappingtools/__init__.py +1 -0
  7. mappingtools-0.3.1/src/mappingtools/_tools.py +10 -0
  8. mappingtools-0.3.1/src/mappingtools/collectors.py +162 -0
  9. mappingtools-0.3.1/src/mappingtools/operators.py +157 -0
  10. mappingtools-0.3.1/src/mappingtools/transformers/__init__.py +7 -0
  11. mappingtools-0.3.1/src/mappingtools/transformers/listify.py +39 -0
  12. mappingtools-0.3.1/src/mappingtools/transformers/simplify.py +15 -0
  13. mappingtools-0.3.1/src/mappingtools/transformers/strictify.py +49 -0
  14. mappingtools-0.3.1/src/mappingtools/transformers/stringify.py +51 -0
  15. mappingtools-0.3.1/src/mappingtools/transformers/transformer.py +75 -0
  16. mappingtools-0.3.1/src/mappingtools/typing.py +7 -0
  17. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/collectors}/test_category_counter.py +2 -1
  18. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/collectors}/test_mapping_collector.py +2 -1
  19. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/collectors}/test_mapping_collector_collect.py +2 -1
  20. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/collectors}/test_mapping_collector_repr.py +2 -1
  21. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/collectors}/test_nested_defaultdict.py +2 -2
  22. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_distinct.py +1 -1
  23. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_flattened.py +16 -10
  24. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_inverse.py +1 -1
  25. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_keep.py +2 -1
  26. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_remove.py +1 -1
  27. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_stream.py +1 -1
  28. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_stream_dict_records.py +2 -1
  29. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/operators}/test_stream_namedtuples.py +2 -1
  30. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/transformers}/test_listify.py +1 -1
  31. {mappingtools-0.2.2/tests → mappingtools-0.3.1/tests/transformers}/test_simplify.py +43 -13
  32. mappingtools-0.3.1/tests/transformers/test_stringify.py +257 -0
  33. mappingtools-0.2.2/src/mappingtools.py +0 -424
  34. {mappingtools-0.2.2 → mappingtools-0.3.1}/.github/dependabot.yml +0 -0
  35. {mappingtools-0.2.2 → mappingtools-0.3.1}/.github/workflows/publish.yml +0 -0
  36. {mappingtools-0.2.2 → mappingtools-0.3.1}/.github/workflows/test.yml +0 -0
  37. {mappingtools-0.2.2 → mappingtools-0.3.1}/LICENSE +0 -0
@@ -16,7 +16,7 @@ jobs:
16
16
  fail-fast: false
17
17
  matrix:
18
18
  # https://github.com/actions/python-versions/blob/main/versions-manifest.json
19
- python-version: [ "3.14.0-alpha.1" ]
19
+ python-version: [ "3.14.0-alpha.4" ]
20
20
  steps:
21
21
  - uses: actions/checkout@v4
22
22
  - name: Set up Python ${{ matrix.python-version }}
@@ -884,3 +884,5 @@ cython_debug/
884
884
  # option (not recommended) you can uncomment the following to ignore the entire idea folder.
885
885
  #.idea/
886
886
 
887
+
888
+ .qodo
@@ -1,7 +1,7 @@
1
- Metadata-Version: 2.3
1
+ Metadata-Version: 2.4
2
2
  Name: mappingtools
3
- Version: 0.2.2
4
- Summary: MappingTools. Do stuff with Mappings
3
+ Version: 0.3.1
4
+ Summary: MappingTools. Do stuff with Mappings and more
5
5
  Project-URL: Homepage, https://erivlis.github.io/mappingtools
6
6
  Project-URL: Bug Tracker, https://github.com/erivlis/mappingtools/issues
7
7
  Project-URL: Source, https://github.com/erivlis/mappingtools
@@ -43,6 +43,10 @@ Description-Content-Type: text/markdown
43
43
 
44
44
  # MappingTools
45
45
 
46
+ > Do stuff with Mappings and more!!!
47
+
48
+ ## Introdcution
49
+
46
50
  This library provides utility functions for manipulating and transforming data structures which have or include
47
51
  Mapping-like characteristics. Including inverting dictionaries, converting class like objects to dictionaries, creating
48
52
  nested defaultdicts, and unwrapping complex objects.
@@ -95,13 +99,79 @@ nested defaultdicts, and unwrapping complex objects.
95
99
  <br>
96
100
  <a href="https://app.codacy.com/gh/erivlis/mappingtools/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade"><img alt="Codacy Quality" src="https://app.codacy.com/project/badge/Grade/8b83a99f939b4883ae2f37d7ec3419d1"></a>
97
101
  <a href="https://app.codacy.com/gh/erivlis/mappingtools/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage"><img alt="Codacy Coverage" src="https://app.codacy.com/project/badge/Coverage/8b83a99f939b4883ae2f37d7ec3419d1"/></a>
102
+ <br>
103
+ <a href="https://www.codefactor.io/repository/github/erivlis/mappingtools/overview/main"><img src="https://www.codefactor.io/repository/github/erivlis/mappingtools/badge/main" alt="CodeFactor" /></a>
104
+ <br>
105
+ <a href="https://snyk.io/test/github/erivlis/mappingtools"><img alt="Snyk" src="https://snyk.io/test/github/erivlis/mappingtools/badge.svg"></a>
98
106
  </td>
99
107
  </tr>
100
108
  </table>
101
109
 
102
110
  ## Usage
103
111
 
104
- ### Transformers
112
+ ### Collectors
113
+
114
+ Collectors are classes that collect data items into a Mapping.
115
+
116
+ #### `CategoryCounter`
117
+
118
+ The CategoryCounter class extends a dictionary to count occurrences of data items categorized by multiple categories.
119
+ It maintains a total count of all data items and allows categorization using direct values or functions.
120
+
121
+ <!-- name: test_category_counter -->
122
+
123
+ ```python
124
+ from mappingtools.collectors import CategoryCounter
125
+
126
+ counter = CategoryCounter()
127
+
128
+ for fruit in ['apple', 'banana', 'apple']:
129
+ counter.update({fruit: 1}, type='fruit', char_count=len(fruit), unique_char_count=len(set(fruit)))
130
+
131
+ print(counter.total)
132
+ # Output: Counter({'apple': 2, 'banana': 1})
133
+
134
+ print(counter)
135
+ # Output: CategoryCounter({'type': defaultdict(<class 'collections.Counter'>, {'fruit': Counter({'apple': 2, 'banana': 1})}), 'char_count': defaultdict(<class 'collections.Counter'>, {5: Counter({'apple': 2}), 6: Counter({'banana': 1})}), 'unique_char_count': defaultdict(<class 'collections.Counter'>, {4: Counter({'apple': 2}), 3: Counter({'banana': 1})})})
136
+ ```
137
+
138
+ #### `MappingCollector`
139
+
140
+ A class designed to collect key-value pairs into an internal mapping based on different modes.
141
+ It supports modes like ALL, COUNT, DISTINCT, FIRST, and LAST, each dictating how key-value pairs are
142
+ collected.
143
+
144
+ <!-- name: test_mapping_collector -->
145
+
146
+ ```python
147
+ from mappingtools.collectors import MappingCollector, MappingCollectorMode
148
+
149
+ collector = MappingCollector(MappingCollectorMode.ALL)
150
+ collector.add('a', 1)
151
+ collector.add('a', 2)
152
+ collector.collect([('b', 3), ('b', 4)])
153
+ print(collector.mapping)
154
+ # Output: {'a': [1, 2], 'b': [3, 4]}
155
+ ```
156
+
157
+ #### `nested_defaultdict`
158
+
159
+ Creates a nested defaultdict with specified depth and factory.
160
+
161
+ <!-- name: test_nested_defaultdict -->
162
+
163
+ ```python
164
+ from mappingtools.collectors import nested_defaultdict
165
+
166
+ nested_dd = nested_defaultdict(1, list)
167
+ nested_dd[0][1].append('value')
168
+ print(nested_dd)
169
+ # Output: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {0: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {1: ['value']})})
170
+ ```
171
+
172
+ ### Operators
173
+
174
+ Operators are functions that perform operations on Mappings.
105
175
 
106
176
  #### `distinct`
107
177
 
@@ -110,7 +180,7 @@ Yields distinct values for a specified key across multiple mappings.
110
180
  <!-- name: test_distinct -->
111
181
 
112
182
  ```python
113
- from mappingtools import distinct
183
+ from mappingtools.operators import distinct
114
184
 
115
185
  mappings = [
116
186
  {'a': 1, 'b': 2},
@@ -129,7 +199,7 @@ Yields subsets of mappings by retaining only the specified keys.
129
199
  <!-- name: test_keep -->
130
200
 
131
201
  ```python
132
- from mappingtools import keep
202
+ from mappingtools.operators import keep
133
203
 
134
204
  mappings = [
135
205
  {'a': 1, 'b': 2, 'c': 3},
@@ -148,7 +218,7 @@ of mappings with those keys excluded.
148
218
  <!-- name: test_remove -->
149
219
 
150
220
  ```python
151
- from mappingtools import remove
221
+ from mappingtools.operators import remove
152
222
 
153
223
  mappings = [
154
224
  {'a': 1, 'b': 2, 'c': 3},
@@ -166,7 +236,7 @@ Swaps keys and values in a dictionary.
166
236
  <!-- name: test_inverse -->
167
237
 
168
238
  ```python
169
- from mappingtools import inverse
239
+ from mappingtools.operators import inverse
170
240
 
171
241
  original_mapping = {'a': {1, 2}, 'b': {3}}
172
242
  inverted_mapping = inverse(original_mapping)
@@ -182,7 +252,7 @@ keys into tuples.
182
252
  <!-- name: test_flattened -->
183
253
 
184
254
  ```python
185
- from mappingtools import flattened
255
+ from mappingtools.operators import flattened
186
256
 
187
257
  nested_dict = {
188
258
  'a': {'b': 1, 'c': {'d': 2}},
@@ -192,97 +262,6 @@ flat_dict = flattened(nested_dict)
192
262
  # Expected output: {('a', 'b'): 1, ('a', 'c', 'd'): 2, ('e',): 3}
193
263
  ```
194
264
 
195
- #### `strictify`
196
-
197
- Strictify function applies a strict structural conversion to an object using optional converters for keys and values.
198
-
199
- <!-- name: test_strictify -->
200
-
201
- ```python
202
- from mappingtools import strictify
203
-
204
-
205
- def uppercase_key(key):
206
- return key.upper()
207
-
208
-
209
- def double_value(value):
210
- return value * 2
211
-
212
-
213
- data = {'a': 1, 'b': 2}
214
- result = strictify(data, key_converter=uppercase_key, value_converter=double_value)
215
- print(result)
216
- # Output: {'A': 2, 'B': 4}
217
- ```
218
-
219
- #### `simplify`
220
-
221
- Converts objects to strictly structured dictionaries.
222
-
223
- <!-- name: test_simplify -->
224
-
225
- ```python
226
- from collections import Counter
227
- from dataclasses import dataclass
228
- from datetime import datetime
229
- from typing import Mapping
230
-
231
- from mappingtools import simplify
232
-
233
- data = {'key1': 'value1', 'key2': ['item1', 'item2']}
234
- simplified_data = simplify(data)
235
- print(simplified_data)
236
- # Output: {'key1': 'value1', 'key2': ['item1', 'item2']}
237
-
238
- counter = Counter({'a': 1, 'b': 2})
239
- print(counter)
240
- # Output: Counter({'b': 2, 'a': 1})
241
-
242
- simplified_counter = simplify(counter)
243
- print(simplified_counter)
244
-
245
-
246
- # Output: {'a': 1, 'b': 2}
247
-
248
-
249
- @dataclass
250
- class SampleDataClass:
251
- a: int
252
- b: int
253
- aa: str
254
- bb: str
255
- c: list[int]
256
- d: Mapping
257
- e: datetime
258
-
259
-
260
- sample_datetime = datetime(2024, 7, 22, 21, 42, 17, 314159)
261
- sample_dataclass = SampleDataClass(1, 2, '11', '22', [1, 2], {'aaa': 111, 'bbb': '222'}, sample_datetime)
262
-
263
- print(sample_dataclass)
264
- # Output: SampleDataClass(a=1, b=2, aa='11', bb='22', c=[1, 2], d={'aaa': 111, 'bbb': '222'}, e=datetime.datetime(2024, 7, 22, 21, 42, 17, 314159))
265
-
266
- simplified_sample_dataclass = simplify(sample_dataclass)
267
- print(simplified_sample_dataclass)
268
- # Output: {'a': 1, 'aa': '11', 'b': 2, 'bb': '22', 'c': [1, 2], 'd': {'aaa': 111, 'bbb': '222'}, 'e': datetime.datetime(2024, 7, 22, 21, 42, 17, 314159)}
269
- ```
270
-
271
- #### `listify`
272
-
273
- Transforms complex objects into a list of dictionaries with key and value pairs.
274
-
275
- <!-- name: test_listify -->
276
-
277
- ```python
278
- from mappingtools import listify
279
-
280
- wrapped_data = {'key1': {'subkey': 'value'}, 'key2': ['item1', 'item2']}
281
- unwrapped_data = listify(wrapped_data)
282
- print(unwrapped_data)
283
- # Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]
284
- ```
285
-
286
265
  #### `stream`
287
266
 
288
267
  Takes a mapping and an optional item factory function, and generates items from the mapping.
@@ -293,7 +272,7 @@ If the item factory is provided, it applies the factory to each key-value pair b
293
272
  ```python
294
273
  from collections import namedtuple
295
274
 
296
- from mappingtools import stream
275
+ from mappingtools.operators import stream
297
276
 
298
277
 
299
278
  def custom_factory(key, value):
@@ -341,7 +320,7 @@ customizable key and value names.
341
320
  <!-- name: test_stream_dict_records -->
342
321
 
343
322
  ```python
344
- from mappingtools import stream_dict_records
323
+ from mappingtools.operators import stream_dict_records
345
324
 
346
325
  mapping = {'a': 1, 'b': 2}
347
326
  records = stream_dict_records(mapping, key_name='letter', value_name='number')
@@ -352,62 +331,121 @@ for record in records:
352
331
  # {'letter': 'b', 'number': 2}
353
332
  ```
354
333
 
355
- ### Collectors
334
+ ### Transformers
356
335
 
357
- #### `nested_defaultdict`
336
+ Transformers are functions that reshape an object, while maintaining the consistency of the structure.
358
337
 
359
- Creates a nested defaultdict with specified depth and factory.
338
+ #### `listify`
360
339
 
361
- <!-- name: test_nested_defaultdict -->
340
+ Transforms complex objects into a list of dictionaries with key and value pairs.
341
+
342
+ <!-- name: test_listify -->
362
343
 
363
344
  ```python
364
- from mappingtools import nested_defaultdict
345
+ from mappingtools.transformers import listify
365
346
 
366
- nested_dd = nested_defaultdict(1, list)
367
- nested_dd[0][1].append('value')
368
- print(nested_dd)
369
- # Output: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {0: defaultdict(<function nested_defaultdict.<locals>.factory at ...>, {1: ['value']})})
347
+ wrapped_data = {'key1': {'subkey': 'value'}, 'key2': ['item1', 'item2']}
348
+ unwrapped_data = listify(wrapped_data)
349
+ print(unwrapped_data)
350
+ # Output: [{'key': 'key1', 'value': [{'key': 'subkey', 'value': 'value'}]}, {'key': 'key2', 'value': ['item1', 'item2']}]
370
351
  ```
371
352
 
372
- #### `CategoryCounter`
353
+ #### `simplify`
373
354
 
374
- The CategoryCounter class extends a dictionary to count occurrences of data items categorized by multiple categories.
375
- It maintains a total count of all data items and allows categorization using direct values or functions.
355
+ Converts objects to strictly structured dictionaries.
376
356
 
377
- <!-- name: test_category_counter -->
357
+ <!-- name: test_simplify -->
378
358
 
379
359
  ```python
380
- from mappingtools import CategoryCounter
381
-
382
- counter = CategoryCounter()
360
+ from collections import Counter
361
+ from dataclasses import dataclass
362
+ from datetime import datetime
363
+ from typing import Mapping
383
364
 
384
- for fruit in ['apple', 'banana', 'apple']:
385
- counter.update({fruit: 1}, type='fruit', char_count=len(fruit), unique_char_count=len(set(fruit)))
365
+ from mappingtools.transformers import simplify
386
366
 
387
- print(counter.total)
388
- # Output: Counter({'apple': 2, 'banana': 1})
367
+ data = {'key1': 'value1', 'key2': ['item1', 'item2']}
368
+ simplified_data = simplify(data)
369
+ print(simplified_data)
370
+ # Output: {'key1': 'value1', 'key2': ['item1', 'item2']}
389
371
 
372
+ counter = Counter({'a': 1, 'b': 2})
390
373
  print(counter)
391
- # Output: CategoryCounter({'type': defaultdict(<class 'collections.Counter'>, {'fruit': Counter({'apple': 2, 'banana': 1})}), 'char_count': defaultdict(<class 'collections.Counter'>, {5: Counter({'apple': 2}), 6: Counter({'banana': 1})}), 'unique_char_count': defaultdict(<class 'collections.Counter'>, {4: Counter({'apple': 2}), 3: Counter({'banana': 1})})})
374
+ # Output: Counter({'b': 2, 'a': 1})
375
+
376
+ simplified_counter = simplify(counter)
377
+ print(simplified_counter)
378
+
379
+
380
+ # Output: {'a': 1, 'b': 2}
381
+
382
+
383
+ @dataclass
384
+ class SampleDataClass:
385
+ a: int
386
+ b: int
387
+ aa: str
388
+ bb: str
389
+ c: list[int]
390
+ d: Mapping
391
+ e: datetime
392
+
393
+
394
+ sample_datetime = datetime(2024, 7, 22, 21, 42, 17, 314159)
395
+ sample_dataclass = SampleDataClass(1, 2, '11', '22', [1, 2], {'aaa': 111, 'bbb': '222'}, sample_datetime)
396
+
397
+ print(sample_dataclass)
398
+ # Output: SampleDataClass(a=1, b=2, aa='11', bb='22', c=[1, 2], d={'aaa': 111, 'bbb': '222'}, e=datetime.datetime(2024, 7, 22, 21, 42, 17, 314159))
399
+
400
+ simplified_sample_dataclass = simplify(sample_dataclass)
401
+ print(simplified_sample_dataclass)
402
+ # Output: {'a': 1, 'aa': '11', 'b': 2, 'bb': '22', 'c': [1, 2], 'd': {'aaa': 111, 'bbb': '222'}, 'e': datetime.datetime(2024, 7, 22, 21, 42, 17, 314159)}
392
403
  ```
393
404
 
394
- #### `MappingCollector`
405
+ #### `strictify`
395
406
 
396
- A class designed to collect key-value pairs into an internal mapping based on different modes.
397
- It supports modes like ALL, COUNT, DISTINCT, FIRST, and LAST, each dictating how key-value pairs are
398
- collected.
407
+ Applies a strict structural conversion to an object using optional converters for keys and values.
399
408
 
400
- <!-- name: test_mapping_collector -->
409
+ <!-- name: test_strictify -->
401
410
 
402
411
  ```python
403
- from mappingtools import MappingCollector, MappingCollectorMode
412
+ from mappingtools.transformers import strictify
404
413
 
405
- collector = MappingCollector(MappingCollectorMode.ALL)
406
- collector.add('a', 1)
407
- collector.add('a', 2)
408
- collector.collect([('b', 3), ('b', 4)])
409
- print(collector.mapping)
410
- # Output: {'a': [1, 2], 'b': [3, 4]}
414
+
415
+ def uppercase_key(key):
416
+ return key.upper()
417
+
418
+
419
+ def double_value(value):
420
+ return value * 2
421
+
422
+
423
+ data = {'a': 1, 'b': 2}
424
+ result = strictify(data, key_converter=uppercase_key, value_converter=double_value)
425
+ print(result)
426
+ # Output: {'A': 2, 'B': 4}
427
+ ```
428
+
429
+ #### `stringify`
430
+
431
+ Converts an object into a string representation by recursively processing it based on its type.
432
+
433
+ <!-- Name: test_stringify -->
434
+
435
+ ```python
436
+ from mappingtools.transformers import stringify
437
+
438
+ data = {'key1': 'value1', 'key2': 'value2'}
439
+ result = stringify(data)
440
+
441
+ print(result)
442
+ # Output: "key1=value1, key2=value2"
443
+
444
+ data = [1, 2, 3]
445
+ result = stringify(data)
446
+
447
+ print(result)
448
+ # Output: "[1, 2, 3]"
411
449
  ```
412
450
 
413
451
  ## Development
@@ -416,6 +454,8 @@ print(collector.mapping)
416
454
 
417
455
  ```shell
418
456
  ruff check src
457
+
458
+ ruff check tests
419
459
  ```
420
460
 
421
461
  ### Test
@@ -430,4 +470,4 @@ python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-re
430
470
 
431
471
  ```shell
432
472
  python -m pytest tests -n auto --cov=src --cov-branch --doctest-modules --cov-report=html
433
- ```
473
+ ```