nadap 2.1.2__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.
Files changed (61) hide show
  1. nadap/__init__.py +13 -0
  2. nadap/base.py +233 -0
  3. nadap/doc.py +686 -0
  4. nadap/errors.py +28 -0
  5. nadap/mixin/accuracy.py +93 -0
  6. nadap/mixin/allow_duplicate.py +86 -0
  7. nadap/mixin/allowed_value.py +97 -0
  8. nadap/mixin/ip.py +349 -0
  9. nadap/mixin/min_max.py +209 -0
  10. nadap/mixin/not_allowed_value.py +99 -0
  11. nadap/mixin/ranges.py +203 -0
  12. nadap/mixin/regex_mode.py +42 -0
  13. nadap/mixin/replace_empty_to.py +39 -0
  14. nadap/nadap.py +86 -0
  15. nadap/references.py +538 -0
  16. nadap/results.py +75 -0
  17. nadap/schema.py +517 -0
  18. nadap/types/any.py +42 -0
  19. nadap/types/base.py +598 -0
  20. nadap/types/bgp_as.py +295 -0
  21. nadap/types/bgp_community.py +326 -0
  22. nadap/types/bool.py +41 -0
  23. nadap/types/bool_false.py +45 -0
  24. nadap/types/bool_true.py +45 -0
  25. nadap/types/byte4_value.py +327 -0
  26. nadap/types/dict.py +677 -0
  27. nadap/types/enum.py +48 -0
  28. nadap/types/float.py +63 -0
  29. nadap/types/hostname.py +394 -0
  30. nadap/types/hostname_or_ip.py +258 -0
  31. nadap/types/idlist.py +208 -0
  32. nadap/types/int.py +60 -0
  33. nadap/types/int16.py +73 -0
  34. nadap/types/int32.py +73 -0
  35. nadap/types/int8.py +73 -0
  36. nadap/types/ip4_address.py +56 -0
  37. nadap/types/ip4_interface.py +59 -0
  38. nadap/types/ip4_network.py +56 -0
  39. nadap/types/ip6_address.py +56 -0
  40. nadap/types/ip6_interface.py +59 -0
  41. nadap/types/ip6_network.py +56 -0
  42. nadap/types/ip_address.py +57 -0
  43. nadap/types/ip_interface.py +68 -0
  44. nadap/types/ip_network.py +55 -0
  45. nadap/types/list.py +145 -0
  46. nadap/types/mac_address.py +407 -0
  47. nadap/types/multitype.py +181 -0
  48. nadap/types/multitype2.py +175 -0
  49. nadap/types/none.py +34 -0
  50. nadap/types/number.py +63 -0
  51. nadap/types/str.py +262 -0
  52. nadap/types/str_float.py +87 -0
  53. nadap/types/str_int.py +79 -0
  54. nadap/types/str_number.py +87 -0
  55. nadap/types/uint16.py +73 -0
  56. nadap/types/uint32.py +73 -0
  57. nadap/types/uint8.py +73 -0
  58. nadap-2.1.2.dist-info/METADATA +52 -0
  59. nadap-2.1.2.dist-info/RECORD +61 -0
  60. nadap-2.1.2.dist-info/WHEEL +4 -0
  61. nadap-2.1.2.dist-info/licenses/LICENSE +674 -0
nadap/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """
2
+ Central import for types
3
+ """
4
+
5
+ from nadap.nadap import Nadap
6
+ from nadap.errors import SchemaDefinitionError, DataValidationError
7
+ from nadap.schema import Schema
8
+ from nadap.references import ReferenceFinding
9
+ from nadap.results import ValidationFinding
10
+ from nadap.base import SET_DEFAULTS, CONVERT_DATA, NOFLAG
11
+
12
+ __version__ = "2.1.2"
13
+ version_info = (2, 1, 2)
nadap/base.py ADDED
@@ -0,0 +1,233 @@
1
+ """
2
+ Basic constants and functions
3
+ """
4
+
5
+ # pylint: disable=too-few-public-methods
6
+
7
+ import enum
8
+ import copy
9
+ import re
10
+
11
+ if hasattr(re, "NOFLAG"):
12
+ NOFLAG = re.NOFLAG
13
+ else:
14
+ NOFLAG = 0
15
+
16
+
17
+ class OPT(enum.IntFlag): # pylint: disable=no-member
18
+ """
19
+ Flags used to define a reference
20
+ """
21
+
22
+ NONE = 1
23
+ UNIQUE = 2
24
+ UNIQUE_GLOBAL = 4
25
+ PRODUCER = 8
26
+ PRODUCER_GLOBAL = 16
27
+ CONSUMER = 32
28
+ CONSUMER_GLOBAL = 64
29
+ ALLOW_ORPHAN_PRODUCER = 128
30
+
31
+
32
+ INIT_OPTIONS = OPT.UNIQUE | OPT.UNIQUE_GLOBAL | OPT.PRODUCER | OPT.PRODUCER_GLOBAL
33
+
34
+
35
+ class RegexObject:
36
+ """
37
+ Represents a regex allowed or not allowed value
38
+ """
39
+
40
+ def __init__(self, pattern: str, multiline: bool, fullmatch: bool):
41
+ self.multiline = multiline
42
+ self.fullmatch = fullmatch
43
+ self.pattern = re.compile(pattern, self.flags)
44
+
45
+ @property
46
+ def flags(self) -> list:
47
+ """
48
+ Create a list of re args to pass to match
49
+ """
50
+ if self.multiline:
51
+ return re.MULTILINE
52
+ return NOFLAG
53
+
54
+ def match(self, data: any) -> re.Match:
55
+ """
56
+ Test data if it matches
57
+ Returns:
58
+ re.Match object or None
59
+ """
60
+ if self.fullmatch:
61
+ return self.pattern.fullmatch(data)
62
+ return self.pattern.search(data)
63
+
64
+ def __hash__(self):
65
+ return self.pattern.pattern.__hash__()
66
+
67
+
68
+ class PreProcessingFlag(enum.IntFlag): # pylint: disable=no-member
69
+ """
70
+ Flags used as pre-processing options
71
+ """
72
+
73
+ NOFLAG = 0
74
+ SET_DEFAULTS = 1
75
+ CONVERT_DATA = 2
76
+
77
+
78
+ class ValEnv:
79
+ """
80
+ Contains information and references required during
81
+ validation and pre-processing
82
+ """
83
+
84
+ def __init__(
85
+ self,
86
+ references,
87
+ findings,
88
+ flags: PreProcessingFlag = PreProcessingFlag.NOFLAG,
89
+ ):
90
+ self.references = references
91
+ self.findings = findings
92
+ self.flags = flags
93
+
94
+
95
+ NOFLAG = PreProcessingFlag.NOFLAG
96
+ SET_DEFAULTS = PreProcessingFlag.SET_DEFAULTS
97
+ CONVERT_DATA = PreProcessingFlag.CONVERT_DATA
98
+ UNDEFINED = object()
99
+
100
+
101
+ def str_list_out(l: list) -> str:
102
+ """
103
+ creates a str from a list, representing an proper output
104
+ """
105
+ if len(l) == 0:
106
+ return ""
107
+ if len(l) == 1:
108
+ return f"'{str(l[0])}'"
109
+ return "'" + "', '".join([str(x) for x in l[:-1]]) + f"' or '{str(l[-1])}'"
110
+
111
+
112
+ def number_to_str_number(x: any) -> any:
113
+ """
114
+ Test if x is a str and represents an integer.
115
+ If yes return a string 'x', else return x
116
+ """
117
+ if isinstance(x, str):
118
+ try:
119
+ int(x)
120
+ x = f"'{x}'"
121
+ except ValueError:
122
+ try:
123
+ float(x)
124
+ x = f"'{x}'"
125
+ except ValueError:
126
+ pass
127
+ return x
128
+
129
+
130
+ def merge_lists(left: list, right: list, list_merge: str = "append_rp") -> list:
131
+ """
132
+ Merge elements of list 'right' into list 'left'
133
+ """
134
+ if not isinstance(left, list):
135
+ raise ValueError("'left' is not a list")
136
+ if not isinstance(right, list):
137
+ raise ValueError("'right' is not a list")
138
+ _left = copy.deepcopy(left)
139
+ if list_merge == "replace":
140
+ return copy.deepcopy(right)
141
+ if list_merge == "append":
142
+ return _left + copy.deepcopy(right)
143
+ if list_merge == "prepend":
144
+ return copy.deepcopy(right) + _left
145
+ if list_merge in ["append_rp", "prepend_rp"]:
146
+ append = list_merge == "append_rp"
147
+ for element in right:
148
+ if element not in _left:
149
+ if append:
150
+ _left.append(copy.deepcopy(element))
151
+ else:
152
+ _left.insert(0, copy.deepcopy(element))
153
+ return _left
154
+ raise ValueError(f"'list_merge' option {list_merge} is not known")
155
+
156
+
157
+ def merge_dictionaries(
158
+ left: dict, right: dict, recursive: bool = True, list_merge: str = "append_rp"
159
+ ) -> dict:
160
+ """
161
+ Merge keys (recursively) from dictionary 'right' into dictionary 'left'.
162
+ """
163
+ if not isinstance(left, dict):
164
+ raise ValueError("'left' is not a dictionary")
165
+ if not isinstance(right, dict):
166
+ raise ValueError("'right' is not a dictionary")
167
+ _left = copy.deepcopy(left)
168
+ for key, r_value in right.items():
169
+ if key not in _left or not isinstance(r_value, type(_left[key])):
170
+ # right element not in left or mismatching types => Just copy right to left
171
+ _left[key] = copy.deepcopy(r_value)
172
+ else:
173
+ # left value is same type as right value
174
+ if isinstance(r_value, list):
175
+ # Merge left list with right list
176
+ _left[key] = merge_lists(
177
+ left=_left[key], right=right[key], list_merge=list_merge
178
+ )
179
+ elif isinstance(r_value, dict):
180
+ if recursive:
181
+ # Merge left dict with right dict
182
+ _left[key] = merge_dictionaries(
183
+ left=_left[key],
184
+ right=right[key],
185
+ recursive=recursive,
186
+ list_merge=list_merge,
187
+ )
188
+ else:
189
+ # Overwrite left with right value
190
+ _left[key] = copy.deepcopy(r_value)
191
+ else:
192
+ # Something like integer or string and just copy right to left
193
+ _left[key] = copy.deepcopy(r_value)
194
+ return _left
195
+
196
+
197
+ def merge_template_data(
198
+ template_data: dict,
199
+ additional_data: dict,
200
+ recursive: bool = True,
201
+ list_merge: str = "append_rp",
202
+ ) -> dict:
203
+ """
204
+ Merge definition from 'additional' template into template.
205
+ """
206
+ template_data = template_data.copy()
207
+ template_data_keys = set(list(template_data.keys()))
208
+ additional_data_keys = set(list(additional_data.keys()))
209
+ return_dict = template_data
210
+ for key in template_data_keys.intersection(additional_data_keys):
211
+ if isinstance(additional_data[key], list):
212
+ if isinstance(template_data[key], list):
213
+ return_dict[key] = merge_lists(
214
+ left=template_data[key],
215
+ right=additional_data[key],
216
+ list_merge=list_merge,
217
+ )
218
+ else:
219
+ return_dict[key] = additional_data[key]
220
+ elif not isinstance(additional_data[key], dict) or not isinstance(
221
+ template_data[key], dict
222
+ ):
223
+ return_dict[key] = additional_data[key]
224
+ else:
225
+ return_dict[key] = merge_dictionaries(
226
+ left=template_data[key],
227
+ right=additional_data[key],
228
+ recursive=recursive,
229
+ list_merge=list_merge,
230
+ )
231
+ for key in additional_data_keys - template_data_keys:
232
+ return_dict[key] = additional_data[key]
233
+ return return_dict