encommon 0.18.0__py3-none-any.whl → 0.19.1__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.
- encommon/parse/__init__.py +20 -0
- encommon/parse/jinja2.py +332 -0
- encommon/parse/network.py +453 -0
- encommon/parse/test/__init__.py +6 -0
- encommon/parse/test/test_jinja2.py +190 -0
- encommon/parse/test/test_network.py +226 -0
- encommon/types/__init__.py +4 -0
- encommon/types/dicts.py +22 -3
- encommon/types/lists.py +84 -0
- encommon/types/test/__init__.py +4 -2
- encommon/types/test/test_dicts.py +45 -7
- encommon/types/test/test_lists.py +41 -0
- encommon/version.txt +1 -1
- {encommon-0.18.0.dist-info → encommon-0.19.1.dist-info}/METADATA +4 -2
- {encommon-0.18.0.dist-info → encommon-0.19.1.dist-info}/RECORD +18 -12
- {encommon-0.18.0.dist-info → encommon-0.19.1.dist-info}/WHEEL +1 -1
- {encommon-0.18.0.dist-info → encommon-0.19.1.dist-info}/LICENSE +0 -0
- {encommon-0.18.0.dist-info → encommon-0.19.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
Functions and routines associated with Enasis Network Common Library.
|
3
|
+
|
4
|
+
This file is part of Enasis Network software eco-system. Distribution
|
5
|
+
is permitted, for more information consult the project license file.
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
from .jinja2 import Jinja2
|
11
|
+
from .network import Network
|
12
|
+
from .network import insubnet_ip
|
13
|
+
from .network import isvalid_ip
|
14
|
+
|
15
|
+
|
16
|
+
__all__ = [
|
17
|
+
'Jinja2',
|
18
|
+
'Network',
|
19
|
+
'insubnet_ip',
|
20
|
+
'isvalid_ip']
|
encommon/parse/jinja2.py
ADDED
@@ -0,0 +1,332 @@
|
|
1
|
+
"""
|
2
|
+
Functions and routines associated with Enasis Network Common Library.
|
3
|
+
|
4
|
+
This file is part of Enasis Network software eco-system. Distribution
|
5
|
+
is permitted, for more information consult the project license file.
|
6
|
+
"""
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
from ast import literal_eval as leval
|
11
|
+
from contextlib import suppress
|
12
|
+
from copy import copy
|
13
|
+
from copy import deepcopy
|
14
|
+
from re import DOTALL
|
15
|
+
from re import findall as re_findall
|
16
|
+
from re import match as re_match
|
17
|
+
from typing import Any
|
18
|
+
from typing import Callable
|
19
|
+
from typing import Optional
|
20
|
+
|
21
|
+
from jinja2 import Environment
|
22
|
+
from jinja2 import StrictUndefined
|
23
|
+
|
24
|
+
from .network import Network
|
25
|
+
from .network import insubnet_ip
|
26
|
+
from .network import isvalid_ip
|
27
|
+
from ..colors import Color
|
28
|
+
from ..crypts import Hashes
|
29
|
+
from ..times import Duration
|
30
|
+
from ..times import Time
|
31
|
+
from ..times import unitime
|
32
|
+
from ..types import DictStrAny
|
33
|
+
from ..types import dedup_list
|
34
|
+
from ..types import fuzzy_list
|
35
|
+
from ..types import hasstr
|
36
|
+
from ..types import inlist
|
37
|
+
from ..types import instr
|
38
|
+
from ..types import merge_dicts
|
39
|
+
from ..types import rplstr
|
40
|
+
from ..types import sort_dict
|
41
|
+
from ..types import strplwr
|
42
|
+
from ..utils import fuzz_match
|
43
|
+
from ..utils import rgxp_match
|
44
|
+
|
45
|
+
|
46
|
+
|
47
|
+
FILTER = Callable[..., Any]
|
48
|
+
|
49
|
+
JINJA2 = (
|
50
|
+
r'(\{\{.+?\}\})|'
|
51
|
+
r'(\{\%.+?\%\})')
|
52
|
+
|
53
|
+
LITERAL = (
|
54
|
+
r'^((\{([^\{%].+?)?\})|(\[(.+?|)\])'
|
55
|
+
'|True|False|None|'
|
56
|
+
r'(\-?([1-9]\d*|0)(\.\d+)?))$')
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
DEFAULT: dict[str, FILTER] = {
|
61
|
+
|
62
|
+
# Python builtins
|
63
|
+
'all': all,
|
64
|
+
'any': any,
|
65
|
+
'copy': copy,
|
66
|
+
'deepcopy': deepcopy,
|
67
|
+
|
68
|
+
# encommon.times
|
69
|
+
'Duration': Duration,
|
70
|
+
'Time': Time,
|
71
|
+
|
72
|
+
# encommon.colors
|
73
|
+
'Color': Color,
|
74
|
+
|
75
|
+
# encommon.crypts
|
76
|
+
'Hashes': Hashes,
|
77
|
+
|
78
|
+
# encommon.parse
|
79
|
+
'Network': Network,
|
80
|
+
'insubnet_ip': insubnet_ip,
|
81
|
+
'isvalid_ip': isvalid_ip,
|
82
|
+
|
83
|
+
# encommon.times
|
84
|
+
'unitime': unitime,
|
85
|
+
|
86
|
+
# encommon.types
|
87
|
+
'strplwr': strplwr,
|
88
|
+
'hasstr': hasstr,
|
89
|
+
'instr': instr,
|
90
|
+
'inlist': inlist,
|
91
|
+
'rplstr': rplstr,
|
92
|
+
'dedup_list': dedup_list,
|
93
|
+
'fuzzy_list': fuzzy_list,
|
94
|
+
'merge_dicts': merge_dicts,
|
95
|
+
'sort_dict': sort_dict,
|
96
|
+
|
97
|
+
# encommon.utils
|
98
|
+
'fuzz_match': fuzz_match,
|
99
|
+
'rgxp_match': rgxp_match}
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
class Jinja2:
|
104
|
+
"""
|
105
|
+
Parse the provided input and intelligently return value.
|
106
|
+
|
107
|
+
Example
|
108
|
+
-------
|
109
|
+
>>> jinja2 = Jinja2()
|
110
|
+
>>> jinja2.parse('{{ 0 | Time}}')
|
111
|
+
'1970-01-01T00:00:00.000000+0000'
|
112
|
+
|
113
|
+
:param statics: Additional values available for parsing.
|
114
|
+
:param filters: Additional filter functions for parsing.
|
115
|
+
"""
|
116
|
+
|
117
|
+
__statics: DictStrAny
|
118
|
+
__filters: dict[str, FILTER]
|
119
|
+
|
120
|
+
__jinjenv: Environment
|
121
|
+
|
122
|
+
|
123
|
+
def __init__(
|
124
|
+
self,
|
125
|
+
statics: Optional[DictStrAny] = None,
|
126
|
+
filters: Optional[dict[str, FILTER]] = None,
|
127
|
+
) -> None:
|
128
|
+
"""
|
129
|
+
Initialize instance for class using provided parameters.
|
130
|
+
"""
|
131
|
+
|
132
|
+
statics = dict(statics or {})
|
133
|
+
filters = dict(filters or {})
|
134
|
+
|
135
|
+
items = DEFAULT.items()
|
136
|
+
|
137
|
+
for key, filter in items:
|
138
|
+
filters[key] = filter
|
139
|
+
|
140
|
+
self.__statics = statics
|
141
|
+
self.__filters = filters
|
142
|
+
|
143
|
+
jinjenv = Environment(
|
144
|
+
auto_reload=False,
|
145
|
+
autoescape=False,
|
146
|
+
cache_size=0,
|
147
|
+
extensions=[
|
148
|
+
'jinja2.ext.i18n',
|
149
|
+
'jinja2.ext.loopcontrols',
|
150
|
+
'jinja2.ext.do'],
|
151
|
+
keep_trailing_newline=False,
|
152
|
+
lstrip_blocks=False,
|
153
|
+
newline_sequence='\n',
|
154
|
+
optimized=True,
|
155
|
+
trim_blocks=False,
|
156
|
+
undefined=StrictUndefined)
|
157
|
+
|
158
|
+
jinjenv.filters |= filters
|
159
|
+
|
160
|
+
self.__jinjenv = jinjenv
|
161
|
+
|
162
|
+
|
163
|
+
@property
|
164
|
+
def statics(
|
165
|
+
self,
|
166
|
+
) -> DictStrAny:
|
167
|
+
"""
|
168
|
+
Return the value for the attribute from class instance.
|
169
|
+
|
170
|
+
:returns: Value for the attribute from class instance.
|
171
|
+
"""
|
172
|
+
|
173
|
+
return dict(self.__statics)
|
174
|
+
|
175
|
+
|
176
|
+
@property
|
177
|
+
def filters(
|
178
|
+
self,
|
179
|
+
) -> dict[str, FILTER]:
|
180
|
+
"""
|
181
|
+
Return the value for the attribute from class instance.
|
182
|
+
|
183
|
+
:returns: Value for the attribute from class instance.
|
184
|
+
"""
|
185
|
+
|
186
|
+
return dict(self.__filters)
|
187
|
+
|
188
|
+
|
189
|
+
@property
|
190
|
+
def jinjenv(
|
191
|
+
self,
|
192
|
+
) -> Environment:
|
193
|
+
"""
|
194
|
+
Return the value for the attribute from class instance.
|
195
|
+
|
196
|
+
:returns: Value for the attribute from class instance.
|
197
|
+
"""
|
198
|
+
|
199
|
+
return self.__jinjenv
|
200
|
+
|
201
|
+
|
202
|
+
def parser(
|
203
|
+
self,
|
204
|
+
value: str,
|
205
|
+
statics: Optional[DictStrAny] = None,
|
206
|
+
) -> Any:
|
207
|
+
"""
|
208
|
+
Return the provided input using the Jinja2 environment.
|
209
|
+
|
210
|
+
:param value: Input that will be processed and returned.
|
211
|
+
:param statics: Additional values available for parsing.
|
212
|
+
:returns: Provided input using the Jinja2 environment.
|
213
|
+
"""
|
214
|
+
|
215
|
+
statics = (
|
216
|
+
dict(statics or {})
|
217
|
+
| self.__statics)
|
218
|
+
|
219
|
+
parser = (
|
220
|
+
self.__jinjenv
|
221
|
+
.from_string)
|
222
|
+
|
223
|
+
rendered = (
|
224
|
+
parser(value)
|
225
|
+
.render(**statics))
|
226
|
+
|
227
|
+
return rendered
|
228
|
+
|
229
|
+
|
230
|
+
def parse( # noqa: CFQ004
|
231
|
+
self,
|
232
|
+
value: Any,
|
233
|
+
statics: Optional[DictStrAny] = None,
|
234
|
+
literal: bool = True,
|
235
|
+
) -> Any:
|
236
|
+
"""
|
237
|
+
Return the provided input using the Jinja2 environment.
|
238
|
+
|
239
|
+
:param value: Input that will be processed and returned.
|
240
|
+
:param statics: Additional values available for parsing.
|
241
|
+
:param literal: Determine if Python objects are evaled.
|
242
|
+
:returns: Provided input using the Jinja2 environment.
|
243
|
+
"""
|
244
|
+
|
245
|
+
|
246
|
+
def _final( # noqa: CFQ004
|
247
|
+
value: Any,
|
248
|
+
) -> Any:
|
249
|
+
|
250
|
+
if literal is False:
|
251
|
+
return value
|
252
|
+
|
253
|
+
match = re_match(
|
254
|
+
LITERAL, str(value))
|
255
|
+
|
256
|
+
if match is None:
|
257
|
+
return value
|
258
|
+
|
259
|
+
with suppress(Exception):
|
260
|
+
return leval(value)
|
261
|
+
|
262
|
+
return value
|
263
|
+
|
264
|
+
|
265
|
+
def _parse(
|
266
|
+
value: Any,
|
267
|
+
) -> Any:
|
268
|
+
|
269
|
+
return self.parse(
|
270
|
+
value,
|
271
|
+
statics, literal)
|
272
|
+
|
273
|
+
|
274
|
+
def _parser(
|
275
|
+
value: Any,
|
276
|
+
) -> Any:
|
277
|
+
|
278
|
+
parsed = self.parser(
|
279
|
+
value, statics)
|
280
|
+
|
281
|
+
return _final(parsed)
|
282
|
+
|
283
|
+
|
284
|
+
def _found(
|
285
|
+
value: Any,
|
286
|
+
) -> list[Any]:
|
287
|
+
|
288
|
+
value = str(value)
|
289
|
+
|
290
|
+
return re_findall(
|
291
|
+
JINJA2, value, DOTALL)
|
292
|
+
|
293
|
+
|
294
|
+
if not len(_found(value)):
|
295
|
+
return _final(value)
|
296
|
+
|
297
|
+
|
298
|
+
with suppress(Exception):
|
299
|
+
value = _final(value)
|
300
|
+
|
301
|
+
|
302
|
+
if isinstance(value, dict):
|
303
|
+
|
304
|
+
value = dict(value)
|
305
|
+
|
306
|
+
items = value.items()
|
307
|
+
|
308
|
+
for key, _value in items:
|
309
|
+
|
310
|
+
_value = _parse(_value)
|
311
|
+
|
312
|
+
value[key] = _value
|
313
|
+
|
314
|
+
|
315
|
+
elif isinstance(value, list):
|
316
|
+
|
317
|
+
value = list(value)
|
318
|
+
|
319
|
+
values = enumerate(value)
|
320
|
+
|
321
|
+
for idx, _value in values:
|
322
|
+
|
323
|
+
_value = _parse(_value)
|
324
|
+
|
325
|
+
value[idx] = _value
|
326
|
+
|
327
|
+
|
328
|
+
elif value is not None:
|
329
|
+
value = _parser(value)
|
330
|
+
|
331
|
+
|
332
|
+
return value
|