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.
- nadap/__init__.py +13 -0
- nadap/base.py +233 -0
- nadap/doc.py +686 -0
- nadap/errors.py +28 -0
- nadap/mixin/accuracy.py +93 -0
- nadap/mixin/allow_duplicate.py +86 -0
- nadap/mixin/allowed_value.py +97 -0
- nadap/mixin/ip.py +349 -0
- nadap/mixin/min_max.py +209 -0
- nadap/mixin/not_allowed_value.py +99 -0
- nadap/mixin/ranges.py +203 -0
- nadap/mixin/regex_mode.py +42 -0
- nadap/mixin/replace_empty_to.py +39 -0
- nadap/nadap.py +86 -0
- nadap/references.py +538 -0
- nadap/results.py +75 -0
- nadap/schema.py +517 -0
- nadap/types/any.py +42 -0
- nadap/types/base.py +598 -0
- nadap/types/bgp_as.py +295 -0
- nadap/types/bgp_community.py +326 -0
- nadap/types/bool.py +41 -0
- nadap/types/bool_false.py +45 -0
- nadap/types/bool_true.py +45 -0
- nadap/types/byte4_value.py +327 -0
- nadap/types/dict.py +677 -0
- nadap/types/enum.py +48 -0
- nadap/types/float.py +63 -0
- nadap/types/hostname.py +394 -0
- nadap/types/hostname_or_ip.py +258 -0
- nadap/types/idlist.py +208 -0
- nadap/types/int.py +60 -0
- nadap/types/int16.py +73 -0
- nadap/types/int32.py +73 -0
- nadap/types/int8.py +73 -0
- nadap/types/ip4_address.py +56 -0
- nadap/types/ip4_interface.py +59 -0
- nadap/types/ip4_network.py +56 -0
- nadap/types/ip6_address.py +56 -0
- nadap/types/ip6_interface.py +59 -0
- nadap/types/ip6_network.py +56 -0
- nadap/types/ip_address.py +57 -0
- nadap/types/ip_interface.py +68 -0
- nadap/types/ip_network.py +55 -0
- nadap/types/list.py +145 -0
- nadap/types/mac_address.py +407 -0
- nadap/types/multitype.py +181 -0
- nadap/types/multitype2.py +175 -0
- nadap/types/none.py +34 -0
- nadap/types/number.py +63 -0
- nadap/types/str.py +262 -0
- nadap/types/str_float.py +87 -0
- nadap/types/str_int.py +79 -0
- nadap/types/str_number.py +87 -0
- nadap/types/uint16.py +73 -0
- nadap/types/uint32.py +73 -0
- nadap/types/uint8.py +73 -0
- nadap-2.1.2.dist-info/METADATA +52 -0
- nadap-2.1.2.dist-info/RECORD +61 -0
- nadap-2.1.2.dist-info/WHEEL +4 -0
- 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
|