idict-schema 1.0.0__py3-none-any.whl

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.
idict/__init__.py ADDED
@@ -0,0 +1,26 @@
1
+ from abc import ABC
2
+ from typing import TypeVar
3
+
4
+ from .idict import Idict
5
+
6
+
7
+ class Stringifable(ABC):
8
+ """
9
+ Stringifable
10
+ """
11
+
12
+ def __str__(self) -> str:
13
+ """
14
+ __str__
15
+
16
+ Returns:
17
+ str
18
+ """
19
+ raise NotImplementedError()
20
+
21
+
22
+ KT = TypeVar("KT", str, Stringifable)
23
+ KV = TypeVar("KV")
24
+ A = TypeVar("A", str, Stringifable)
25
+
26
+ __all__ = ["Idict", "KT", "KV", "A", "Stringifable"]
idict/__main__.py ADDED
@@ -0,0 +1,2 @@
1
+ if __name__ == "__main__":
2
+ pass
idict/exceptions.py ADDED
@@ -0,0 +1,17 @@
1
+ from lib.exceptions import (
2
+ EllipsisException,
3
+ GeneralException,
4
+ KeyNotAllowedException,
5
+ KeyOnNonDictException,
6
+ MandatoryKeyValueException,
7
+ ValueNotAllowedException,
8
+ )
9
+
10
+ __all__ = [
11
+ "EllipsisException",
12
+ "GeneralException",
13
+ "KeyNotAllowedException",
14
+ "KeyOnNonDictException",
15
+ "MandatoryKeyValueException",
16
+ "ValueNotAllowedException",
17
+ ]
idict/idict.py ADDED
@@ -0,0 +1,3 @@
1
+ from lib.idict import Idict
2
+
3
+ __all__ = ["Idict"]
idict/utils.py ADDED
@@ -0,0 +1,3 @@
1
+ from lib.utils import Utils
2
+
3
+ __all__ = ["Utils"]
@@ -0,0 +1,347 @@
1
+ Metadata-Version: 2.4
2
+ Name: idict-schema
3
+ Version: 1.0.0
4
+ Summary: Extended defaultdict with declared structure and validation rules
5
+ Home-page: https://github.com/hadasbro/idict
6
+ Author: Slawomir Hadas
7
+ Author-email: slawekgithub1218@gmail.com
8
+ License: GPL-3.0-only
9
+ Project-URL: Source, https://github.com/hadasbro/idict
10
+ Project-URL: Issues, https://github.com/hadasbro/idict/issues
11
+ Keywords: dict,defaultdict,validation,schema
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.7
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Requires-Python: >=3.7
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: keywords
32
+ Dynamic: license
33
+ Dynamic: license-file
34
+ Dynamic: project-url
35
+ Dynamic: requires-python
36
+ Dynamic: summary
37
+
38
+ # I - Dict
39
+
40
+ ![picture](https://img.shields.io/badge/Python-3.7.0%2B-brightgreen)
41
+ ![picture](https://img.shields.io/badge/License-GNU%20GPL%203.0-blue)
42
+
43
+ **Dictionary with declared structure in Python**
44
+
45
+ Tiny extension for [defaultdict](https://docs.python.org/3.3/library/collections.html#collections.defaultdict) Python's class.
46
+ Use this library if you need to ensure or force concrete
47
+ structure for your dictionary and use it as normal dictionary then.
48
+
49
+ ---
50
+
51
+ ## Install
52
+
53
+ Install from PyPI:
54
+
55
+ ```bash
56
+ pip install idict-schema
57
+ ```
58
+
59
+ or from sources:
60
+
61
+ ```bash
62
+ pip install .
63
+ ```
64
+
65
+ After installation, import from:
66
+
67
+ ```python
68
+ from idict import Idict
69
+ ```
70
+
71
+ ---
72
+
73
+ ## Overview
74
+
75
+ You can provide desired dictionary's structure and specify
76
+ action for non desired usage e.g. specify action if someone
77
+ uses unexpected key or value.
78
+
79
+ You can also specify required and/or default values for specified keys.
80
+
81
+ ## 1.0 Contract
82
+
83
+ - Distribution name: `idict-schema`
84
+ - Import path: `idict` (`from idict import Idict`)
85
+ - `missing_keys = THROW` is enforced for `[] =`, `update`, `setdefault`, `|`, and `|=`
86
+ - Mandatory keys (`...`) are validated against the declared schema (`validate()`)
87
+ - `copy.copy(idict_obj)` is supported
88
+ - `pickle` serialization is currently not supported and raises a clear `TypeError`
89
+ - `hash_code()` returns Base64 of `repr(dict(...))` (it is not a cryptographic hash)
90
+
91
+ #### General usage
92
+ ```python
93
+ from idict import Idict
94
+
95
+ desired_default_structure = {
96
+ 'a': 1,
97
+ 'c': None
98
+ }
99
+
100
+ options = {
101
+ "missing_keys": Idict.OPT.THROW,
102
+ "ellipsis_as_mandatory": True
103
+ }
104
+
105
+ my_dict = Idict(desired_default_structure, options)
106
+
107
+ my_dict['a'] = 1
108
+
109
+ print(repr(my_dict))
110
+ # {'a': 1, 'c': None}
111
+ ```
112
+
113
+ #### Options
114
+
115
+ **[ missing_keys ]** - action if unspecified key usage
116
+ ```python
117
+ options = {
118
+ "missing_keys": Idict.OPT.ALLOW
119
+ }
120
+ ```
121
+ possible options: `Idict.OPT.`
122
+ - `ALLOW` [default value] - allow all other keys and just add to the dictionary.
123
+ - `IGNORE` - ignore keys not available in specified interface and allow only specified keys. Always keep the same, desired structure. Ignore tries to change dictionary structure.
124
+ - `THROW` - throw an exception `KeyNotAllowedException` if someone tries to set key-value for unspecified key.
125
+ Throw an exception `ValueNotAllowedException` if someone tries to change specified key-value structure.
126
+ See examples below.
127
+
128
+ Key not allowed:
129
+
130
+ ```python
131
+ my_dict = Idict({'a': None, 'b': {'bb1': None}}, {"missing_keys": Idict.OPT.THROW})
132
+ my_dict['non_existing_key'] = 123
133
+
134
+ '''
135
+ KeyNotAllowedException
136
+ [Key Error] You are trying to set value for key <non_existing> for your Idict
137
+ <your_dict:Idict> but this key is disallowed and doesnt exist in the provided interface
138
+ '''
139
+ ```
140
+
141
+ Trying to overwritte expected structure and set integer where dictionary should be:
142
+
143
+ ```python
144
+ my_dict = Idict({'a': {'aa': {'aaa': 1}}}, options={"missing_keys": Idict.OPT.THROW})
145
+ my_dict['a']['aa'] = 123
146
+
147
+ '''
148
+ ValueNotAllowedException:
149
+ [Value Error] Trying to set value <123> for object , key <aa>
150
+ but in provided interface there is a dictionary under this key - {'aa': {'aaa': 1}}.
151
+ You cannot change expected objects structure
152
+ '''
153
+ ```
154
+
155
+ **[ ellipsis_as_mandatory ]** - [default = True] use Ellipsis (`...`) as a tag for mandatory keys.
156
+ If this option is settled to `True` then, upon validation, library will check if every key with `Ellipsis` value has new value settled.
157
+ Dictionary will be valid as long as every value tagged by `...` has new value. Otherwise `dict.validate()` will throw an excaption `MandatoryKeyValueException`.
158
+ ```python
159
+ options = {
160
+ "ellipsis_as_mandatory": True
161
+ }
162
+ ```
163
+ possible options:
164
+ - True
165
+ - False
166
+
167
+ Example:
168
+ ```python
169
+ # use Ellipsis - ... as a tag for mandatory values
170
+ my_dict = Idict({'a': {'aa': None}, 'b': ...}, options={"ellipsis_as_mandatory": True})
171
+
172
+ my_dict.validate()
173
+
174
+ # THROWS:
175
+ # MandatoryKeyValueException: Key value for key [b]
176
+ # is mandatory. Set value for this key or set default value in provided interface.
177
+
178
+ my_dict['b'] = "anything"
179
+
180
+ my_dict.validate()
181
+ # OK, no exception, result = True
182
+ ```
183
+
184
+
185
+
186
+ ## Examples
187
+
188
+ + Allow specified and unspecified keys:
189
+
190
+ Default structure and default values are specified in advance.
191
+ We allow other key-value pairs, not only specified ones.
192
+
193
+ ```python
194
+ my_dict = Idict({
195
+ 'a': 1,
196
+ 'b': 2,
197
+ 'c': {
198
+ 'cc': 3,
199
+ },
200
+ 'd': {
201
+ 'dd': {
202
+ 'ddd': 4
203
+ },
204
+ },
205
+ })
206
+
207
+ # set/overwrite some values
208
+ my_dict['a'] = 11
209
+ my_dict['c']['cc'] = 33
210
+ my_dict['d']['dd']['new-value'] = 44
211
+ my_dict['e']['ee'] = 55
212
+
213
+ print(repr(my_dict))
214
+ # {'a': 11, 'b': 2, 'c': {'cc': 33}, 'd': {'dd': {'ddd': 4, 'new-value': 44}}, 'e': {'ee': 55}}
215
+ ```
216
+
217
+
218
+ + Ignore unexpected keys:
219
+
220
+ Allow only specified structure, ignore all other key-value pairs.
221
+
222
+ ```python
223
+ my_dict = Idict({
224
+ 'a': 1,
225
+ 'c': {
226
+ 'cc1': 3,
227
+ 'cc2': 4,
228
+ }
229
+ }, {
230
+ "missing_keys": Idict.OPT.IGNORE,
231
+ })
232
+
233
+ # set/overwrite some values
234
+ my_dict['a'] = 11
235
+ my_dict['b'] = 22
236
+ my_dict['c']['cc1'] = 33
237
+ my_dict['d'] = 55
238
+
239
+
240
+ print(repr(my_dict))
241
+ # {'a': 11, 'c': {'cc1': 33, 'cc2': 4}}
242
+ ```
243
+
244
+ + Allow only specified keys usage or throw an exception `KeyNotAllowedException`
245
+
246
+ Throw an exception (KeyNotAllowedException) in case of trying to set value for disallowed key:
247
+
248
+ ```python
249
+ dict1 = Idict({
250
+ 'a': None,
251
+ 'b': {
252
+ 'bb1': None,
253
+ 'bb2': None
254
+ }
255
+ }, {
256
+ "missing_keys": Idict.OPT.THROW
257
+ })
258
+
259
+ try:
260
+ dict1['non_existing_key'] = "value"
261
+ except KeyNotAllowedException as ex:
262
+ print(ex)
263
+ # [Key Error] You are trying to set value
264
+ # for key for your Idict
265
+ # but this key is disallowed
266
+ # and doesnt exist in the provided interface
267
+ ```
268
+
269
+ + Changing default/provided default dictionary's structure
270
+ ```python
271
+ desired_structure = {
272
+ 'a': 'default value <a>',
273
+ 'b': {
274
+ 'bb1': 'default value ',
275
+ }
276
+ }
277
+
278
+ my_dict = Idict(desired_structure, {
279
+ "missing_keys": Idict.OPT.ALLOW
280
+ })
281
+
282
+ my_dict_ign = Idict(desired_structure, {
283
+ "missing_keys": Idict.OPT.IGNORE
284
+ })
285
+
286
+ my_dict_thr = Idict(desired_structure, {
287
+ "missing_keys": Idict.OPT.THROW
288
+ })
289
+
290
+ my_dict['b'] = 123
291
+ print(repr(my_dict))
292
+ # OK, result: {'a': 'default value <a>', 'b': 123}
293
+
294
+ my_dict_ign['b'] = 123
295
+ print(repr(my_dict_ign))
296
+ # illegal trying to change desired dictionary structure
297
+ # this operation will be ignored
298
+ # OK, no exception, result: {'a': 'default value ', 'b': {'bb1': 'default value '}}
299
+
300
+ my_dict_thr['b'] = 123
301
+ # illegal trying to change desired dictionary structure
302
+ # result: raise ValueNotAllowedException as below
303
+
304
+ # ValueNotAllowedException: [Value Error] Trying to set value <123>
305
+ # for object <your_dict:Idict>, key <b> but in provided interface there
306
+ # is a dictionary under this key - <interface>
307
+ # {'a': 'default value ', 'b': {'bb1': 'default value '}}.
308
+ # You cannot change expected objects structure
309
+ ```
310
+
311
+
312
+ + Validate dictionary, check mandatory keys.
313
+
314
+ Check if all mandatory keys (marked by `Ellipsis` - `...`) are overwritten/settled
315
+
316
+ ```python
317
+ my_dict1 = Idict({
318
+ 'a': None,
319
+ 'b': {
320
+ 'bb1': None,
321
+ 'bb2': ...
322
+ },
323
+ })
324
+
325
+ try:
326
+ my_dict1.validate()
327
+ except MandatoryKeyValueException as e:
328
+ # Key value for key [b][bb2] is mandatory.
329
+ # Set value for this key or set default value in provided interface.
330
+ pass
331
+
332
+ my_dict1['b']['bb2'] = 123
333
+
334
+ print(repr(my_dict1))
335
+ # no Exception, all mandatory keys have value settled
336
+ # {'a': None, 'b': {'bb1': None, 'bb2': 123}}
337
+
338
+ ```
339
+
340
+ ## Authors
341
+
342
+ * **Slawomir Hadas** - *author* - [Github](https://github.com/hadasbro)
343
+
344
+ ## See more
345
+ See `./tests` for more examples
346
+
347
+ ---
@@ -0,0 +1,15 @@
1
+ idict/__init__.py,sha256=4RKh1RgFiVucie5LvABZ3zBNRzqFdI_YB_yBSF7jAlw,449
2
+ idict/__main__.py,sha256=HX4MrDFp3XQJC-6BmcAv_LjtfdQ5rN3NW-zEmKNW6t0,38
3
+ idict/exceptions.py,sha256=uPcG1sGmI33GGm6hfGpAVDi8hcwfbf0PwCNiknICKsg,399
4
+ idict/idict.py,sha256=LCGGhUgDyMOhcTZTKoz8naAiadVkiBYwUgWJ0OH_QV4,52
5
+ idict/utils.py,sha256=y-bDmmrncZswxTZAPEwmny4kX8QNEJshoKLZcPUx8Ac,52
6
+ idict_schema-1.0.0.dist-info/licenses/LICENSE,sha256=IwGE9guuL-ryRPEKi6wFPI_zOhg7zDZbTYuHbSt_SAk,35823
7
+ lib/__init__.py,sha256=JwQQmerbfdlrDaznYPe-EsAGDl9zmQuOBRRIlhXgNpI,365
8
+ lib/__main__.py,sha256=tkZ_iuY7UZngMK-oaQCp920Zz6e1y2WdSbVxPTYo_DA,38
9
+ lib/exceptions.py,sha256=b43nvdTFzf7QvS_fL5XxqdrjXd6hdMMPR98bINJtIUQ,3561
10
+ lib/idict.py,sha256=LljiXiZluX38OFCxi2iBsQfqtjh3-GGDw2zSSlj3FAY,15673
11
+ lib/utils.py,sha256=uQuF1P4S8YhGi3kiZgUGIE3Ci0jwoRHSQ4R4hSVTxg0,6439
12
+ idict_schema-1.0.0.dist-info/METADATA,sha256=UoSPSye7oVsQMbuAj4oTMJdP6H-iVEprEbBod6W9O3A,10013
13
+ idict_schema-1.0.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
14
+ idict_schema-1.0.0.dist-info/top_level.txt,sha256=GtdMigkai9B0AEyjED3GFi-Grj97F4CBkNODAHUF5jA,10
15
+ idict_schema-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+