encommon 0.17.2__py3-none-any.whl → 0.19.0__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,453 @@
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 contextlib import suppress
11
+ from typing import Any
12
+ from typing import Optional
13
+
14
+ from netaddr import IPAddress
15
+ from netaddr import IPNetwork
16
+
17
+
18
+
19
+ class Network:
20
+ """
21
+ Convert the network into the various supported formats.
22
+
23
+ :param source: Network IPv4 or IPv6 network or address.
24
+ """
25
+
26
+ __source: IPNetwork
27
+
28
+
29
+ def __init__(
30
+ self,
31
+ source: str,
32
+ ) -> None:
33
+ """
34
+ Initialize instance for class using provided parameters.
35
+ """
36
+
37
+ network = IPNetwork(source)
38
+
39
+ self.__source = network
40
+
41
+
42
+ def __repr__(
43
+ self,
44
+ ) -> str:
45
+ """
46
+ Built-in method for representing the values for instance.
47
+
48
+ :returns: String representation for values from instance.
49
+ """
50
+
51
+ return f"Network('{self.address_cidr}')"
52
+
53
+
54
+ def __hash__(
55
+ self,
56
+ ) -> int:
57
+ """
58
+ Built-in method called when performing hashing operation.
59
+
60
+ :returns: Boolean indicating outcome from the operation.
61
+ """
62
+
63
+ return hash(self.__source)
64
+
65
+
66
+ def __str__(
67
+ self,
68
+ ) -> str:
69
+ """
70
+ Built-in method for representing the values for instance.
71
+
72
+ :returns: String representation for values from instance.
73
+ """
74
+
75
+ return self.address_cidr
76
+
77
+
78
+ def __eq__(
79
+ self,
80
+ other: object,
81
+ ) -> bool:
82
+ """
83
+ Built-in method for comparing this instance with another.
84
+
85
+ :param other: Other value being compared with instance.
86
+ :returns: Boolean indicating outcome from the operation.
87
+ """
88
+
89
+ source = self.__source
90
+
91
+ with suppress(Exception):
92
+
93
+ other = IPNetwork(str(other))
94
+
95
+ return source == other
96
+
97
+ return False
98
+
99
+
100
+ def __ne__(
101
+ self,
102
+ other: object,
103
+ ) -> bool:
104
+ """
105
+ Built-in method for comparing this instance with another.
106
+
107
+ :param other: Other value being compared with instance.
108
+ :returns: Boolean indicating outcome from the operation.
109
+ """
110
+
111
+ return not self.__eq__(other)
112
+
113
+
114
+ @property
115
+ def source(
116
+ self,
117
+ ) -> IPNetwork:
118
+ """
119
+ Return the value for the attribute from class instance.
120
+
121
+ :returns: Value for the attribute from class instance.
122
+ """
123
+
124
+ return self.__source
125
+
126
+
127
+ @property
128
+ def version(
129
+ self,
130
+ ) -> int:
131
+ """
132
+ Return the value for the attribute from class instance.
133
+
134
+ :returns: Value for the attribute from class instance.
135
+ """
136
+
137
+ return self.__source.version
138
+
139
+
140
+ @property
141
+ def cidr(
142
+ self,
143
+ ) -> int:
144
+ """
145
+ Return the value for the attribute from class instance.
146
+
147
+ :returns: Value for the attribute from class instance.
148
+ """
149
+
150
+ source = self.__source
151
+
152
+ return source.prefixlen
153
+
154
+
155
+ @property
156
+ def address(
157
+ self,
158
+ ) -> str:
159
+ """
160
+ Return the value for the attribute from class instance.
161
+
162
+ :returns: Value for the attribute from class instance.
163
+ """
164
+
165
+ source = self.__source
166
+
167
+ return str(source.ip)
168
+
169
+
170
+ @property
171
+ def address_cidr(
172
+ self,
173
+ ) -> str:
174
+ """
175
+ Return the value for the attribute from class instance.
176
+
177
+ :returns: Value for the attribute from class instance.
178
+ """
179
+
180
+ address = self.address
181
+
182
+ return f'{address}/{self.cidr}'
183
+
184
+
185
+ @property
186
+ def address_host(
187
+ self,
188
+ ) -> str:
189
+ """
190
+ Return the value for the attribute from class instance.
191
+
192
+ :returns: Value for the attribute from class instance.
193
+ """
194
+
195
+ address = self.address
196
+
197
+ prefix = (
198
+ 128
199
+ if self.version == 6
200
+ else 32)
201
+
202
+ return f'{address}/{prefix}'
203
+
204
+
205
+ @property
206
+ def network(
207
+ self,
208
+ ) -> str:
209
+ """
210
+ Return the value for the attribute from class instance.
211
+
212
+ :returns: Value for the attribute from class instance.
213
+ """
214
+
215
+ source = self.__source
216
+
217
+ return str(source.network)
218
+
219
+
220
+ @property
221
+ def network_cidr(
222
+ self,
223
+ ) -> str:
224
+ """
225
+ Return the value for the attribute from class instance.
226
+
227
+ :returns: Value for the attribute from class instance.
228
+ """
229
+
230
+ network = self.network
231
+
232
+ return f'{network}/{self.cidr}'
233
+
234
+
235
+ @property
236
+ def broadcast(
237
+ self,
238
+ ) -> Optional[str]:
239
+ """
240
+ Return the value for the attribute from class instance.
241
+
242
+ :returns: Value for the attribute from class instance.
243
+ """
244
+
245
+ source = self.__source
246
+
247
+ address = source.broadcast
248
+
249
+ if address is None:
250
+ return None
251
+
252
+ return str(address)
253
+
254
+
255
+ @property
256
+ def padded(
257
+ self,
258
+ ) -> str:
259
+ """
260
+ Return the value for the attribute from class instance.
261
+
262
+ :returns: Value for the attribute from class instance.
263
+ """
264
+
265
+ if self.version != 4:
266
+ raise ValueError('version')
267
+
268
+ address = self.address
269
+
270
+ octets = address.split('.')
271
+
272
+ pads = [
273
+ x.zfill(3)
274
+ for x in octets]
275
+
276
+ return '.'.join(pads)
277
+
278
+
279
+ @property
280
+ def reverse(
281
+ self,
282
+ ) -> str:
283
+ """
284
+ Return the value for the attribute from class instance.
285
+
286
+ :returns: Value for the attribute from class instance.
287
+ """
288
+
289
+ if self.version != 4:
290
+ raise ValueError('version')
291
+
292
+
293
+ address = self.address
294
+
295
+ octets = address.split('.')
296
+
297
+ reverse = list(reversed(octets))
298
+
299
+ return '.'.join(reverse)
300
+
301
+
302
+ @property
303
+ def hwaddr(
304
+ self,
305
+ ) -> str:
306
+ """
307
+ Return the value for the attribute from class instance.
308
+
309
+ :returns: Value for the attribute from class instance.
310
+ """
311
+
312
+ if self.version != 4:
313
+ raise ValueError('version')
314
+
315
+ padded = self.padded
316
+
317
+ nodots = (
318
+ padded
319
+ .replace('.', ''))
320
+
321
+ ranged = range(
322
+ 0, len(nodots), 2)
323
+
324
+ pairs = [
325
+ nodots[x:x + 2]
326
+ for x in ranged]
327
+
328
+ return '-'.join(pairs)
329
+
330
+
331
+ @property
332
+ def netmask(
333
+ self,
334
+ ) -> str:
335
+ """
336
+ Return the value for the attribute from class instance.
337
+
338
+ :returns: Value for the attribute from class instance.
339
+ """
340
+
341
+ source = self.__source
342
+
343
+ return str(source.netmask)
344
+
345
+
346
+
347
+ @property
348
+ def ispublic(
349
+ self,
350
+ ) -> bool:
351
+ """
352
+ Return the boolean indicating whether instance is state.
353
+ """
354
+
355
+ return (
356
+ self.source.ip
357
+ .is_global())
358
+
359
+
360
+ @property
361
+ def isprivate(
362
+ self,
363
+ ) -> bool:
364
+ """
365
+ Return the boolean indicating whether instance is state.
366
+ """
367
+
368
+ return not self.ispublic
369
+
370
+
371
+ @property
372
+ def islinklocal(
373
+ self,
374
+ ) -> bool:
375
+ """
376
+ Return the boolean indicating whether instance is state.
377
+ """
378
+
379
+ return (
380
+ self.source
381
+ .is_link_local())
382
+
383
+
384
+ @property
385
+ def islocalhost(
386
+ self,
387
+ ) -> bool:
388
+ """
389
+ Return the boolean indicating whether instance is state.
390
+ """
391
+
392
+ return (
393
+ self.source
394
+ .is_loopback())
395
+
396
+
397
+
398
+ def insubnet_ip(
399
+ address: str,
400
+ networks: str | list[str] | tuple[str, ...],
401
+ ) -> bool:
402
+ """
403
+ Return the boolean indicating address is in the network.
404
+
405
+ :param address: Provided address to find in the network.
406
+ :param network: Networks which values can be within any.
407
+ :returns: Boolean indicating address is in the network.
408
+ """
409
+
410
+ if isinstance(networks, str):
411
+ networks = [networks]
412
+
413
+ networks = list(networks)
414
+
415
+ if not isvalid_ip(address):
416
+ raise ValueError('address')
417
+
418
+ naddr = Network(address)
419
+
420
+ if (naddr.version == 4
421
+ and naddr.cidr == 32):
422
+ address = naddr.address
423
+
424
+ if (naddr.version == 6
425
+ and naddr.cidr == 128):
426
+ address = naddr.address
427
+
428
+ parsed = IPAddress(address)
429
+
430
+ return any(
431
+ parsed in IPNetwork(x)
432
+ for x in networks)
433
+
434
+
435
+
436
+ def isvalid_ip(
437
+ value: Any, # noqa: ANN401
438
+ ) -> bool:
439
+ """
440
+ Return the boolean indicating whether the value is valid.
441
+
442
+ :param value: Value that will be validated as an address.
443
+ :returns: Boolean indicating whether the value is valid.
444
+ """
445
+
446
+ value = str(value)
447
+
448
+ try:
449
+ Network(value)
450
+ return True
451
+
452
+ except Exception:
453
+ return False
@@ -0,0 +1,6 @@
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
+ """
@@ -0,0 +1,190 @@
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 typing import Any
11
+
12
+ from pytest import fixture
13
+ from pytest import mark
14
+
15
+ from ..jinja2 import Jinja2
16
+ from ... import PROJECT
17
+ from ...times.common import UNIXMPOCH
18
+ from ...types import inrepr
19
+ from ...types import instr
20
+ from ...types import lattrs
21
+
22
+
23
+
24
+ @fixture
25
+ def jinja2() -> Jinja2:
26
+ """
27
+ Construct the instance for use in the downstream tests.
28
+
29
+ :returns: Newly constructed instance of related class.
30
+ """
31
+
32
+
33
+ def strval(
34
+ # NOCVR,
35
+ input: str,
36
+ ) -> str:
37
+ return str(input)
38
+
39
+
40
+ return Jinja2(
41
+ {'PROJECT': PROJECT},
42
+ {'strval': strval})
43
+
44
+
45
+
46
+ def test_Jinja2(
47
+ jinja2: Jinja2,
48
+ ) -> None:
49
+ """
50
+ Perform various tests associated with relevant routines.
51
+
52
+ :param jinja2: Parsing class for the Jinja2 templating.
53
+ """
54
+
55
+
56
+ attrs = lattrs(jinja2)
57
+
58
+ assert attrs == [
59
+ '_Jinja2__statics',
60
+ '_Jinja2__filters',
61
+ '_Jinja2__jinjenv']
62
+
63
+
64
+ assert inrepr(
65
+ 'jinja2.Jinja2 object',
66
+ jinja2)
67
+
68
+ assert hash(jinja2) > 0
69
+
70
+ assert instr(
71
+ 'jinja2.Jinja2 object',
72
+ jinja2)
73
+
74
+
75
+ assert jinja2.statics
76
+
77
+ assert jinja2.filters
78
+
79
+ assert jinja2.jinjenv
80
+
81
+ assert jinja2.parse('1') == 1
82
+
83
+
84
+
85
+ def test_Jinja2_recurse(
86
+ jinja2: Jinja2,
87
+ ) -> None:
88
+ """
89
+ Perform various tests associated with relevant routines.
90
+
91
+ :param jinja2: Parsing class for the Jinja2 templating.
92
+ """
93
+
94
+ parse = jinja2.parse
95
+
96
+ source = {
97
+ 'dict': {'key': '{{ "val" }}'},
98
+ 'list': [1, '{{ 2 }}', 3, '4'],
99
+ 'deep': '{{ {"foo": "bar"} }}'}
100
+
101
+ parsed = parse(source)
102
+
103
+ assert parsed == {
104
+ 'dict': {'key': 'val'},
105
+ 'list': [1, 2, 3, 4],
106
+ 'deep': {'foo': 'bar'}}
107
+
108
+ _parsed = parse(f'{source}')
109
+
110
+ assert _parsed == parsed
111
+
112
+
113
+
114
+ @mark.parametrize(
115
+ 'value,expect',
116
+ [('{{ none }}', None),
117
+ ('{{ 123 }}', 123),
118
+ ('{{ 1.2 }}', 1.2),
119
+ ('{{ -1.2 }}', -1.2)])
120
+ def test_Jinja2_literal(
121
+ jinja2: Jinja2,
122
+ value: Any, # noqa: ANN401
123
+ expect: Any, # noqa: ANN401
124
+ ) -> None:
125
+ """
126
+ Perform various tests associated with relevant routines.
127
+
128
+ :param jinja2: Parsing class for the Jinja2 templating.
129
+ :param value: Input that will be processed and returned.
130
+ :param expect: Expected output from the testing routine.
131
+ """
132
+
133
+ parse = jinja2.parse
134
+
135
+ parsed = parse(value)
136
+
137
+ parsed = parse(
138
+ value,
139
+ literal=False)
140
+
141
+ assert parsed == str(expect)
142
+
143
+ parsed = parse(
144
+ value,
145
+ literal=True)
146
+
147
+ assert parsed == expect
148
+
149
+
150
+
151
+ @mark.parametrize(
152
+ 'value,expect',
153
+ [('foo', 'foo'),
154
+ ('1.0.0', '1.0.0'),
155
+ ('1', 1),
156
+ ('1.0', 1.0),
157
+ ({'a': 'b'}, {'a': 'b'}),
158
+ ("{'a': 'b'}", {'a': 'b'}),
159
+ ('{{ 1 }}', 1),
160
+ ('{{ "1" | float }}', 1.0),
161
+ ('{{ "1" | int }}', 1),
162
+ ('{{ 0 | Time }}', UNIXMPOCH),
163
+ (100000, 100000),
164
+ ('100000', 100000),
165
+ (10.001, 10.001),
166
+ ('10.001', 10.001),
167
+ ([1, 2], [1, 2]),
168
+ ('[1, 2]', [1, 2]),
169
+ ('01', '01'),
170
+ ('{{ "01" }}', '01'),
171
+ ('-01', '-01'),
172
+ ('{{ "-01" }}', '-01')])
173
+ def test_Jinja2_cover(
174
+ jinja2: Jinja2,
175
+ value: Any, # noqa: ANN401
176
+ expect: Any, # noqa: ANN401
177
+ ) -> None:
178
+ """
179
+ Perform various tests associated with relevant routines.
180
+
181
+ :param jinja2: Parsing class for the Jinja2 templating.
182
+ :param value: Input that will be processed and returned.
183
+ :param expect: Expected output from the testing routine.
184
+ """
185
+
186
+ parse = jinja2.parse
187
+
188
+ parsed = parse(value)
189
+
190
+ assert parsed == expect