PySerials 0.0.0.dev48__tar.gz → 0.0.0.dev50__tar.gz
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.
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/PKG-INFO +3 -3
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/pyproject.toml +3 -3
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/PySerials.egg-info/PKG-INFO +3 -3
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/PySerials.egg-info/requires.txt +2 -2
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/exception/update.py +4 -3
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/update.py +48 -27
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/write.py +19 -2
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/README.md +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/setup.cfg +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/PySerials.egg-info/SOURCES.txt +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/PySerials.egg-info/dependency_links.txt +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/PySerials.egg-info/not-zip-safe +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/PySerials.egg-info/top_level.txt +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/__init__.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/compare.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/exception/__init__.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/exception/_base.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/exception/read.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/exception/validate.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/format.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/nested_dict.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/property_dict.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/read.py +0 -0
- {pyserials-0.0.0.dev48 → pyserials-0.0.0.dev50}/src/pyserials/validate.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PySerials
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev50
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
5
|
Requires-Dist: jsonschema<5,>=4.21.0
|
|
6
6
|
Requires-Dist: referencing>=0.35.1
|
|
@@ -8,6 +8,6 @@ Requires-Dist: jsonpath-ng<2,>=1.6.1
|
|
|
8
8
|
Requires-Dist: ruamel.yaml<0.18,>=0.17.32
|
|
9
9
|
Requires-Dist: ruamel.yaml.string<1,>=0.1.1
|
|
10
10
|
Requires-Dist: tomlkit<0.12,>=0.11.8
|
|
11
|
-
Requires-Dist: MDit==0.0.0.
|
|
12
|
-
Requires-Dist: ExceptionMan==0.0.0.
|
|
11
|
+
Requires-Dist: MDit==0.0.0.dev47
|
|
12
|
+
Requires-Dist: ExceptionMan==0.0.0.dev47
|
|
13
13
|
Requires-Dist: ProtocolMan==0.0.0.dev2
|
|
@@ -17,7 +17,7 @@ namespaces = true
|
|
|
17
17
|
# ----------------------------------------- Project Metadata -------------------------------------
|
|
18
18
|
#
|
|
19
19
|
[project]
|
|
20
|
-
version = "0.0.0.
|
|
20
|
+
version = "0.0.0.dev50"
|
|
21
21
|
name = "PySerials"
|
|
22
22
|
dependencies = [
|
|
23
23
|
"jsonschema >= 4.21.0, < 5",
|
|
@@ -26,8 +26,8 @@ dependencies = [
|
|
|
26
26
|
"ruamel.yaml >= 0.17.32, < 0.18", # https://yaml.readthedocs.io/en/stable/
|
|
27
27
|
"ruamel.yaml.string >= 0.1.1, < 1",
|
|
28
28
|
"tomlkit >= 0.11.8, < 0.12", # https://tomlkit.readthedocs.io/en/stable/,
|
|
29
|
-
"MDit == 0.0.0.
|
|
30
|
-
"ExceptionMan == 0.0.0.
|
|
29
|
+
"MDit == 0.0.0.dev47",
|
|
30
|
+
"ExceptionMan == 0.0.0.dev47",
|
|
31
31
|
"ProtocolMan == 0.0.0.dev2",
|
|
32
32
|
]
|
|
33
33
|
requires-python = ">=3.10"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: PySerials
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev50
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
5
|
Requires-Dist: jsonschema<5,>=4.21.0
|
|
6
6
|
Requires-Dist: referencing>=0.35.1
|
|
@@ -8,6 +8,6 @@ Requires-Dist: jsonpath-ng<2,>=1.6.1
|
|
|
8
8
|
Requires-Dist: ruamel.yaml<0.18,>=0.17.32
|
|
9
9
|
Requires-Dist: ruamel.yaml.string<1,>=0.1.1
|
|
10
10
|
Requires-Dist: tomlkit<0.12,>=0.11.8
|
|
11
|
-
Requires-Dist: MDit==0.0.0.
|
|
12
|
-
Requires-Dist: ExceptionMan==0.0.0.
|
|
11
|
+
Requires-Dist: MDit==0.0.0.dev47
|
|
12
|
+
Requires-Dist: ExceptionMan==0.0.0.dev47
|
|
13
13
|
Requires-Dist: ProtocolMan==0.0.0.dev2
|
|
@@ -123,14 +123,15 @@ class PySerialsUpdateTemplatedDataError(PySerialsUpdateException):
|
|
|
123
123
|
template_start: str,
|
|
124
124
|
template_end: str,
|
|
125
125
|
):
|
|
126
|
-
self.path_invalid = path_invalid
|
|
126
|
+
self.path_invalid = path_invalid
|
|
127
127
|
self.data_source = data_source
|
|
128
128
|
self.template_start = template_start
|
|
129
129
|
self.template_end = template_end
|
|
130
130
|
parts = description_template.split("{path_invalid}")
|
|
131
|
-
|
|
131
|
+
if len(parts) > 1:
|
|
132
|
+
parts.insert(1, _mdit.element.code_span(str(self.path_invalid)))
|
|
132
133
|
super().__init__(
|
|
133
|
-
path=path
|
|
134
|
+
path=str(path),
|
|
134
135
|
data=data,
|
|
135
136
|
data_full=data_full,
|
|
136
137
|
problem=_mdit.inline_container(*parts),
|
|
@@ -152,6 +152,7 @@ class TemplateFiller:
|
|
|
152
152
|
|
|
153
153
|
self._pattern_value: dict[int, _RegexPattern] = {}
|
|
154
154
|
self._data = None
|
|
155
|
+
self._visited_paths = {}
|
|
155
156
|
return
|
|
156
157
|
|
|
157
158
|
def fill(
|
|
@@ -161,16 +162,17 @@ class TemplateFiller:
|
|
|
161
162
|
current_path: str = "",
|
|
162
163
|
):
|
|
163
164
|
self._data = data
|
|
164
|
-
|
|
165
|
+
self._visited_paths = {}
|
|
166
|
+
path = _jsonpath.parse((f"$.{current_path}" if self._add_prefix else current_path) if current_path else "$")
|
|
165
167
|
return self._recursive_subst(
|
|
166
168
|
templ=template or data,
|
|
167
169
|
current_path=path,
|
|
168
170
|
relative_path_anchor=path,
|
|
169
171
|
level=0,
|
|
170
|
-
current_chain=
|
|
172
|
+
current_chain=(path,),
|
|
171
173
|
)
|
|
172
174
|
|
|
173
|
-
def _recursive_subst(self, templ, current_path: str, relative_path_anchor: str, level: int, current_chain:
|
|
175
|
+
def _recursive_subst(self, templ, current_path: str, relative_path_anchor: str, level: int, current_chain: tuple[str, ...]):
|
|
174
176
|
|
|
175
177
|
def get_code_value(match: _re.Match | str):
|
|
176
178
|
|
|
@@ -198,13 +200,15 @@ class TemplateFiller:
|
|
|
198
200
|
local_context = {}
|
|
199
201
|
try:
|
|
200
202
|
exec(code_str_full, global_context, local_context)
|
|
201
|
-
|
|
203
|
+
output = local_context["__inline_code__"]()
|
|
202
204
|
except Exception as e:
|
|
203
205
|
raise_error(
|
|
204
206
|
description_template=f"Code at {{path_invalid}} raised an exception: {e}\n{code_str_full}",
|
|
205
207
|
path_invalid=current_path,
|
|
206
208
|
exception=e,
|
|
207
209
|
)
|
|
210
|
+
self._visited_paths[current_path] = (output, True)
|
|
211
|
+
return output
|
|
208
212
|
|
|
209
213
|
def get_address_value(match: _re.Match | str, return_all_matches: bool = False, from_code: bool = False):
|
|
210
214
|
raw_path = match if isinstance(match, str) else str(match.group(1))
|
|
@@ -215,21 +219,21 @@ class TemplateFiller:
|
|
|
215
219
|
path_expr = _jsonpath.parse(path)
|
|
216
220
|
except _jsonpath_exceptions.JSONPathError:
|
|
217
221
|
raise_error(
|
|
218
|
-
path_invalid=
|
|
222
|
+
path_invalid=path_expr,
|
|
219
223
|
description_template="JSONPath expression {path_invalid} is invalid.",
|
|
220
224
|
)
|
|
221
225
|
if num_periods:
|
|
222
226
|
if relative_path_anchor != current_path:
|
|
223
|
-
path_fields = self._extract_fields(
|
|
227
|
+
path_fields = self._extract_fields(current_path)
|
|
224
228
|
has_template_key = any(field in self._template_keys for field in path_fields)
|
|
225
229
|
anchor_path = relative_path_anchor if has_template_key else current_path
|
|
226
230
|
else:
|
|
227
231
|
anchor_path = current_path
|
|
228
|
-
root_path_expr =
|
|
232
|
+
root_path_expr = anchor_path
|
|
229
233
|
for period in range(num_periods):
|
|
230
234
|
if isinstance(root_path_expr, _jsonpath.Root):
|
|
231
235
|
raise_error(
|
|
232
|
-
path_invalid=
|
|
236
|
+
path_invalid=path_expr,
|
|
233
237
|
description_template=(
|
|
234
238
|
"Relative path {path_invalid} is invalid; "
|
|
235
239
|
f"reached root but still {num_periods - period} levels remaining."
|
|
@@ -237,7 +241,8 @@ class TemplateFiller:
|
|
|
237
241
|
)
|
|
238
242
|
root_path_expr = root_path_expr.left
|
|
239
243
|
path_expr = self._concat_json_paths(root_path_expr, path_expr)
|
|
240
|
-
value, matched = get_value(path_expr, return_all_matches, from_code)
|
|
244
|
+
value, matched = self._visited_paths.get(path_expr) or get_value(path_expr, return_all_matches, from_code)
|
|
245
|
+
self._visited_paths[path_expr] = (value, matched)
|
|
241
246
|
if from_code:
|
|
242
247
|
return value, matched
|
|
243
248
|
if matched:
|
|
@@ -255,7 +260,7 @@ class TemplateFiller:
|
|
|
255
260
|
return [], True
|
|
256
261
|
if self._raise_no_match:
|
|
257
262
|
raise_error(
|
|
258
|
-
path_invalid=
|
|
263
|
+
path_invalid=jsonpath,
|
|
259
264
|
description_template="JSONPath expression {path_invalid} did not match any data.",
|
|
260
265
|
)
|
|
261
266
|
return None, False
|
|
@@ -269,10 +274,10 @@ class TemplateFiller:
|
|
|
269
274
|
_rel_path_anchor = relative_path_anchor
|
|
270
275
|
return self._recursive_subst(
|
|
271
276
|
output,
|
|
272
|
-
current_path=
|
|
277
|
+
current_path=jsonpath,
|
|
273
278
|
relative_path_anchor=_rel_path_anchor,
|
|
274
279
|
level=0,
|
|
275
|
-
current_chain=current_chain +
|
|
280
|
+
current_chain=current_chain + (jsonpath,),
|
|
276
281
|
), True
|
|
277
282
|
|
|
278
283
|
def _rec_match(expr) -> list:
|
|
@@ -286,10 +291,10 @@ class TemplateFiller:
|
|
|
286
291
|
for left_match in left_matches:
|
|
287
292
|
left_match_filled = self._recursive_subst(
|
|
288
293
|
templ=left_match.value,
|
|
289
|
-
current_path=
|
|
290
|
-
relative_path_anchor=
|
|
294
|
+
current_path=expr.left,
|
|
295
|
+
relative_path_anchor=expr.left,
|
|
291
296
|
level=0,
|
|
292
|
-
current_chain=current_chain +
|
|
297
|
+
current_chain=current_chain + (expr.left,),
|
|
293
298
|
) if isinstance(left_match.value, str) else left_match.value
|
|
294
299
|
right_matches = expr.right.find(left_match_filled)
|
|
295
300
|
whole_matches.extend(right_matches)
|
|
@@ -336,6 +341,9 @@ class TemplateFiller:
|
|
|
336
341
|
template_end=self._marker_end_value,
|
|
337
342
|
) from exception
|
|
338
343
|
|
|
344
|
+
if current_path in self._visited_paths:
|
|
345
|
+
return self._visited_paths[current_path][0]
|
|
346
|
+
|
|
339
347
|
self._check_endless_loop(templ, current_chain)
|
|
340
348
|
|
|
341
349
|
if isinstance(templ, str):
|
|
@@ -384,18 +392,25 @@ class TemplateFiller:
|
|
|
384
392
|
if isinstance(templ, list):
|
|
385
393
|
out = []
|
|
386
394
|
for idx, elem in enumerate(templ):
|
|
387
|
-
new_path =
|
|
395
|
+
new_path = _jsonpath.Child(current_path, _jsonpath.Index(idx))
|
|
388
396
|
elem_filled = self._recursive_subst(
|
|
389
397
|
templ=elem,
|
|
390
398
|
current_path=new_path,
|
|
391
399
|
relative_path_anchor=get_relative_path(new_path),
|
|
392
400
|
level=0,
|
|
393
|
-
current_chain=current_chain +
|
|
401
|
+
current_chain=current_chain + (new_path,),
|
|
394
402
|
)
|
|
395
403
|
if isinstance(elem, str) and self._pattern_unpack.fullmatch(elem):
|
|
396
|
-
|
|
404
|
+
try:
|
|
405
|
+
out.extend(elem_filled)
|
|
406
|
+
except TypeError as e:
|
|
407
|
+
raise_error(
|
|
408
|
+
path_invalid=current_path,
|
|
409
|
+
description_template=str(e)
|
|
410
|
+
)
|
|
397
411
|
else:
|
|
398
412
|
out.append(elem_filled)
|
|
413
|
+
self._visited_paths[current_path] = (out, True)
|
|
399
414
|
return out
|
|
400
415
|
|
|
401
416
|
if isinstance(templ, dict):
|
|
@@ -414,28 +429,30 @@ class TemplateFiller:
|
|
|
414
429
|
if key_filled in self._template_keys:
|
|
415
430
|
new_dict[key_filled] = val
|
|
416
431
|
continue
|
|
417
|
-
new_path =
|
|
432
|
+
new_path = _jsonpath.Child(current_path, _jsonpath.Fields(key_filled))
|
|
418
433
|
new_dict[key_filled] = self._recursive_subst(
|
|
419
434
|
templ=val,
|
|
420
435
|
current_path=new_path,
|
|
421
436
|
relative_path_anchor=get_relative_path(new_path),
|
|
422
437
|
level=0,
|
|
423
|
-
current_chain=current_chain +
|
|
438
|
+
current_chain=current_chain + (new_path,),
|
|
424
439
|
)
|
|
440
|
+
self._visited_paths[current_path] = (new_dict, True)
|
|
425
441
|
return new_dict
|
|
426
442
|
return templ
|
|
427
443
|
|
|
428
|
-
def _check_endless_loop(self,templ, chain:
|
|
429
|
-
last_idx = len(chain) -1
|
|
444
|
+
def _check_endless_loop(self,templ, chain: tuple[str, ...]):
|
|
445
|
+
last_idx = len(chain) - 1
|
|
430
446
|
first_idx = chain.index(chain[-1])
|
|
431
447
|
if first_idx == last_idx:
|
|
432
448
|
return
|
|
433
|
-
loop = chain[
|
|
434
|
-
loop_str = "\n".join([f"- {path
|
|
449
|
+
loop = [chain[-2], *chain[first_idx: -2]]
|
|
450
|
+
loop_str = "\n".join([f"- {path}" for path in loop])
|
|
451
|
+
history_str = "\n".join([f"- {path}" for path in chain])
|
|
435
452
|
raise _exception.update.PySerialsUpdateTemplatedDataError(
|
|
436
|
-
description_template=f"Path {{path_invalid}} starts a loop:\n{loop_str}",
|
|
437
|
-
path_invalid=
|
|
438
|
-
path=chain[
|
|
453
|
+
description_template=f"Path {{path_invalid}} starts a loop:\n{loop_str}\nHistory:\n{history_str}",
|
|
454
|
+
path_invalid=chain[-2],
|
|
455
|
+
path=chain[0],
|
|
439
456
|
data=templ,
|
|
440
457
|
data_full=self._data,
|
|
441
458
|
data_source=self._data,
|
|
@@ -480,6 +497,10 @@ class TemplateFiller:
|
|
|
480
497
|
_recursive_extract(jsonpath)
|
|
481
498
|
return fields
|
|
482
499
|
|
|
500
|
+
@staticmethod
|
|
501
|
+
def _normalize_path(path: str) -> str:
|
|
502
|
+
return path.replace("'", "")
|
|
503
|
+
|
|
483
504
|
def _concat_json_paths(self, path1, path2):
|
|
484
505
|
if not isinstance(path2, _jsonpath.Child):
|
|
485
506
|
return _jsonpath.Child(path1, path2)
|
|
@@ -2,6 +2,7 @@ from typing import Literal as _Literal
|
|
|
2
2
|
from pathlib import Path as _Path
|
|
3
3
|
import json as _json
|
|
4
4
|
import ruamel.yaml as _yaml
|
|
5
|
+
from ruamel.yaml import scalarstring as _yaml_scalar_string
|
|
5
6
|
import tomlkit as _tomlkit
|
|
6
7
|
|
|
7
8
|
|
|
@@ -22,8 +23,16 @@ def to_string(
|
|
|
22
23
|
def to_yaml_string(
|
|
23
24
|
data: dict | list | str | int | float | bool | _yaml.CommentedMap | _yaml.CommentedSeq,
|
|
24
25
|
end_of_file_newline: bool = True,
|
|
26
|
+
indent_mapping: int = 2,
|
|
27
|
+
indent_sequence: int = 4,
|
|
28
|
+
indent_sequence_offset: int = 2,
|
|
29
|
+
multiline_string_to_block: bool = True,
|
|
25
30
|
) -> str:
|
|
26
|
-
|
|
31
|
+
yaml = _yaml.YAML(typ=["rt", "string"])
|
|
32
|
+
yaml.indent(mapping=indent_mapping, sequence=indent_sequence, offset=indent_sequence_offset)
|
|
33
|
+
if multiline_string_to_block:
|
|
34
|
+
_yaml_scalar_string.walk_tree(data)
|
|
35
|
+
yaml_syntax = yaml.dumps(data, add_final_eol=False).removesuffix("\n...")
|
|
27
36
|
return f"{yaml_syntax}\n" if end_of_file_newline else yaml_syntax
|
|
28
37
|
|
|
29
38
|
|
|
@@ -46,9 +55,17 @@ def to_yaml_file(
|
|
|
46
55
|
data: dict | list | str | int | float | bool | _yaml.CommentedMap | _yaml.CommentedSeq,
|
|
47
56
|
path: str | _Path,
|
|
48
57
|
make_dirs: bool = True,
|
|
58
|
+
indent_mapping: int = 2,
|
|
59
|
+
indent_sequence: int = 4,
|
|
60
|
+
indent_sequence_offset: int = 2,
|
|
61
|
+
multiline_string_to_block: bool = True,
|
|
49
62
|
):
|
|
50
63
|
path = _Path(path).resolve()
|
|
51
64
|
if make_dirs:
|
|
52
65
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
53
|
-
_yaml.YAML()
|
|
66
|
+
yaml = _yaml.YAML()
|
|
67
|
+
yaml.indent(mapping=indent_mapping, sequence=indent_sequence, offset=indent_sequence_offset)
|
|
68
|
+
if multiline_string_to_block:
|
|
69
|
+
_yaml_scalar_string.walk_tree(data)
|
|
70
|
+
yaml.dump(data, path)
|
|
54
71
|
return
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|