PySerials 0.0.0.dev29__py3-none-any.whl → 0.0.0.dev31__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.
- {PySerials-0.0.0.dev29.dist-info → PySerials-0.0.0.dev31.dist-info}/METADATA +3 -3
- {PySerials-0.0.0.dev29.dist-info → PySerials-0.0.0.dev31.dist-info}/RECORD +6 -6
- pyserials/nested_dict.py +66 -21
- pyserials/update.py +213 -85
- {PySerials-0.0.0.dev29.dist-info → PySerials-0.0.0.dev31.dist-info}/WHEEL +0 -0
- {PySerials-0.0.0.dev29.dist-info → PySerials-0.0.0.dev31.dist-info}/top_level.txt +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.dev31
|
|
4
4
|
Requires-Python: >=3.10
|
|
5
5
|
Requires-Dist: jsonschema <5,>=4.21.0
|
|
6
6
|
Requires-Dist: referencing >=0.35.1
|
|
@@ -8,7 +8,7 @@ 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.dev28
|
|
12
|
+
Requires-Dist: ExceptionMan ==0.0.0.dev28
|
|
13
13
|
Requires-Dist: ProtocolMan ==0.0.0.dev2
|
|
14
14
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
pyserials/__init__.py,sha256=-ySdqDuoUXdi2Pa8uuFa5m1CTAtbZS3SWc5qzaOdR5o,142
|
|
2
2
|
pyserials/compare.py,sha256=j62A1UIiAm08_xONlbZmU2EcH1GMEpDyEQH66dZ2YMM,1297
|
|
3
3
|
pyserials/format.py,sha256=dTukpab6WHSyVRQ9SteY5fhr3GFjWFboEl-1cw_udVY,1729
|
|
4
|
-
pyserials/nested_dict.py,sha256=
|
|
4
|
+
pyserials/nested_dict.py,sha256=NYF4SlhwTWAWmFpDrlMZN3qe5TxpQSToDX258iuHk_M,5992
|
|
5
5
|
pyserials/read.py,sha256=uucYQH1V4GStwRgRZ2eQIXkH4ukB5qz0EA885grwi68,6592
|
|
6
|
-
pyserials/update.py,sha256=
|
|
6
|
+
pyserials/update.py,sha256=nVGWlzb7YlEBdz91FVS2n9tV6NfIN_EwF243sg-sWxA,18786
|
|
7
7
|
pyserials/validate.py,sha256=ti0D_yLzB_HELvf1d5qrarx1Ac-opBMN1Xh5lADRAQU,3664
|
|
8
8
|
pyserials/write.py,sha256=pN8w78qVsKJjZd_jvPUcZjYp_RJkP7uQzpiXvPOv4lM,1776
|
|
9
9
|
pyserials/exception/__init__.py,sha256=ZhbggwJUMlTyBhifAivC8ZQxP1Na6lJAwzZs7_YjOSU,151
|
|
@@ -11,7 +11,7 @@ pyserials/exception/_base.py,sha256=IdaZwBPBYgiUaWnvN0eMXvQQBqLbN1t766034CK7Hlc,
|
|
|
11
11
|
pyserials/exception/read.py,sha256=QyG6ulExXH9u8oDRjUfter70SMDVQqL4nig5s-JzWN4,9252
|
|
12
12
|
pyserials/exception/update.py,sha256=P0js2-iY2fgO_KLdqedgWE3TTS5Xz15cusZY_wuKIW4,4222
|
|
13
13
|
pyserials/exception/validate.py,sha256=7UkQEEqCa8HJ20gpTFnLDhT3P5OPLD2oD9fUK2Jcuns,7466
|
|
14
|
-
PySerials-0.0.0.
|
|
15
|
-
PySerials-0.0.0.
|
|
16
|
-
PySerials-0.0.0.
|
|
17
|
-
PySerials-0.0.0.
|
|
14
|
+
PySerials-0.0.0.dev31.dist-info/METADATA,sha256=_q6JchXWJtGk0S2szc-hRZILQZxhK5nHhls3Z0eQXqM,438
|
|
15
|
+
PySerials-0.0.0.dev31.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
16
|
+
PySerials-0.0.0.dev31.dist-info/top_level.txt,sha256=SAks7WjSjdkv3i9Hvt4gY_P7VQbhhYJN5mf5dqx1aao,10
|
|
17
|
+
PySerials-0.0.0.dev31.dist-info/RECORD,,
|
pyserials/nested_dict.py
CHANGED
|
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING as _TYPE_CHECKING
|
|
|
5
5
|
import pyserials as _ps
|
|
6
6
|
|
|
7
7
|
if _TYPE_CHECKING:
|
|
8
|
-
from typing import Callable
|
|
8
|
+
from typing import Callable, Any
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class NestedDict:
|
|
@@ -13,31 +13,59 @@ class NestedDict:
|
|
|
13
13
|
def __init__(
|
|
14
14
|
self,
|
|
15
15
|
data: dict | None = None,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
marker_start_value: str = "$",
|
|
17
|
+
marker_end_value: str = "$",
|
|
18
|
+
repeater_start_value: str = "{",
|
|
19
|
+
repeater_end_value: str = "}",
|
|
20
|
+
repeater_count_value: int = 2,
|
|
21
|
+
start_list: str = "$[[",
|
|
22
|
+
start_unpack: str = "*{{",
|
|
23
|
+
start_code: str = "#{{",
|
|
24
|
+
end_list: str = "]]$",
|
|
25
|
+
end_unpack: str = "}}*",
|
|
26
|
+
end_code: str = "}}#",
|
|
27
|
+
recursive: bool = True,
|
|
28
|
+
raise_no_match: bool = True,
|
|
29
|
+
leave_no_match: bool = False,
|
|
30
|
+
no_match_value: Any = None,
|
|
31
|
+
stringer: Callable[[str], str] = str,
|
|
32
|
+
unpack_string_joiner: str = ", ",
|
|
22
33
|
relative_template_keys: list[str] | None = None,
|
|
34
|
+
implicit_root: bool = True,
|
|
23
35
|
):
|
|
24
36
|
self._data = data or {}
|
|
25
37
|
self._templater = _ps.update.TemplateFiller(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
38
|
+
marker_start_value=marker_start_value,
|
|
39
|
+
marker_end_value=marker_end_value,
|
|
40
|
+
repeater_start_value=repeater_start_value,
|
|
41
|
+
repeater_end_value=repeater_end_value,
|
|
42
|
+
repeater_count_value=repeater_count_value,
|
|
43
|
+
start_list=start_list,
|
|
44
|
+
start_unpack=start_unpack,
|
|
45
|
+
start_code=start_code,
|
|
46
|
+
end_list=end_list,
|
|
47
|
+
end_unpack=end_unpack,
|
|
48
|
+
end_code=end_code,
|
|
32
49
|
)
|
|
33
|
-
self.
|
|
50
|
+
self._recursive = recursive
|
|
51
|
+
self._raise_no_match = raise_no_match
|
|
52
|
+
self._leave_no_match = leave_no_match
|
|
53
|
+
self._no_match_value = no_match_value
|
|
54
|
+
self._stringer = stringer
|
|
55
|
+
self._unpack_string_joiner = unpack_string_joiner
|
|
56
|
+
self._relative_template_keys = relative_template_keys or []
|
|
57
|
+
self._implicit_root = implicit_root
|
|
34
58
|
return
|
|
35
59
|
|
|
36
60
|
def fill(
|
|
37
61
|
self,
|
|
38
62
|
path: str = "",
|
|
39
|
-
|
|
40
|
-
|
|
63
|
+
recursive: bool | None = None,
|
|
64
|
+
raise_no_match: bool | None = None,
|
|
65
|
+
leave_no_match: bool | None = None,
|
|
66
|
+
stringer: Callable[[str], str] | None = None,
|
|
67
|
+
unpack_string_joiner: str | None = None,
|
|
68
|
+
level: int = 0,
|
|
41
69
|
):
|
|
42
70
|
if not path:
|
|
43
71
|
value = self._data
|
|
@@ -46,7 +74,14 @@ class NestedDict:
|
|
|
46
74
|
if not value:
|
|
47
75
|
return
|
|
48
76
|
filled_value = self.fill_data(
|
|
49
|
-
data=value,
|
|
77
|
+
data=value,
|
|
78
|
+
current_path=path,
|
|
79
|
+
recursive=recursive,
|
|
80
|
+
raise_no_match=raise_no_match,
|
|
81
|
+
leave_no_match=leave_no_match,
|
|
82
|
+
stringer=stringer,
|
|
83
|
+
unpack_string_joiner=unpack_string_joiner,
|
|
84
|
+
level=level,
|
|
50
85
|
)
|
|
51
86
|
if not path:
|
|
52
87
|
self._data = filled_value
|
|
@@ -58,16 +93,26 @@ class NestedDict:
|
|
|
58
93
|
self,
|
|
59
94
|
data,
|
|
60
95
|
current_path: str = "",
|
|
61
|
-
|
|
62
|
-
|
|
96
|
+
recursive: bool | None = None,
|
|
97
|
+
raise_no_match: bool | None = None,
|
|
98
|
+
leave_no_match: bool | None = None,
|
|
99
|
+
stringer: Callable[[str], str] | None = None,
|
|
100
|
+
unpack_string_joiner: str | None = None,
|
|
101
|
+
level: int = 0,
|
|
63
102
|
):
|
|
64
103
|
return self._templater.fill(
|
|
65
104
|
templated_data=data,
|
|
66
105
|
source_data=self._data,
|
|
67
106
|
current_path=current_path,
|
|
68
|
-
|
|
69
|
-
|
|
107
|
+
recursive=recursive if recursive is not None else self._recursive,
|
|
108
|
+
raise_no_match=raise_no_match if raise_no_match is not None else self._raise_no_match,
|
|
109
|
+
leave_no_match=leave_no_match if leave_no_match is not None else self._leave_no_match,
|
|
110
|
+
no_match_value=self._no_match_value,
|
|
111
|
+
stringer=stringer if stringer is not None else self._stringer,
|
|
112
|
+
unpack_string_joiner=unpack_string_joiner if unpack_string_joiner is not None else self._unpack_string_joiner,
|
|
70
113
|
relative_template_keys=self._relative_template_keys,
|
|
114
|
+
implicit_root=self._implicit_root,
|
|
115
|
+
level=level,
|
|
71
116
|
)
|
|
72
117
|
|
|
73
118
|
def __call__(self):
|
pyserials/update.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations as _annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
2
4
|
from typing import TYPE_CHECKING as _TYPE_CHECKING
|
|
3
5
|
import re as _re
|
|
4
6
|
|
|
@@ -8,7 +10,7 @@ from jsonpath_ng import exceptions as _jsonpath_exceptions
|
|
|
8
10
|
import pyserials.exception as _exception
|
|
9
11
|
|
|
10
12
|
if _TYPE_CHECKING:
|
|
11
|
-
from typing import Literal,
|
|
13
|
+
from typing import Literal, Sequence, Any, Callable
|
|
12
14
|
|
|
13
15
|
|
|
14
16
|
def dict_from_addon(
|
|
@@ -103,25 +105,29 @@ class TemplateFiller:
|
|
|
103
105
|
|
|
104
106
|
def __init__(
|
|
105
107
|
self,
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
108
|
+
marker_start_value: str = "$",
|
|
109
|
+
marker_end_value: str = "$",
|
|
110
|
+
repeater_start_value: str = "{",
|
|
111
|
+
repeater_end_value: str = "}",
|
|
112
|
+
repeater_count_value: int = 2,
|
|
113
|
+
start_list: str = "$[[",
|
|
114
|
+
start_unpack: str = "*{{",
|
|
115
|
+
start_code: str = "#{{",
|
|
116
|
+
end_list: str = "]]$",
|
|
117
|
+
end_unpack: str = "}}*",
|
|
118
|
+
end_code: str = "}}#",
|
|
112
119
|
):
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
self.
|
|
120
|
-
self.
|
|
121
|
-
self.
|
|
122
|
-
|
|
123
|
-
self.
|
|
124
|
-
self._stringer = stringer
|
|
120
|
+
self._marker_start_value = marker_start_value
|
|
121
|
+
self._marker_end_value = marker_end_value
|
|
122
|
+
self._repeater_start_value = repeater_start_value
|
|
123
|
+
self._repeater_end_value = repeater_end_value
|
|
124
|
+
self._repeater_count_value = repeater_count_value
|
|
125
|
+
self._pattern_list = _RegexPattern(start=start_list, end=end_list)
|
|
126
|
+
self._pattern_unpack = _RegexPattern(start=start_unpack, end=end_unpack)
|
|
127
|
+
self._pattern_code = _RegexPattern(start=start_code, end=end_code)
|
|
128
|
+
self._add_prefix = True
|
|
129
|
+
|
|
130
|
+
self._pattern_value: dict[int, _RegexPattern] = {}
|
|
125
131
|
self._data = None
|
|
126
132
|
self._source = None
|
|
127
133
|
self._recursive = None
|
|
@@ -129,22 +135,48 @@ class TemplateFiller:
|
|
|
129
135
|
self._raise_no_match = None
|
|
130
136
|
self._template_keys = None
|
|
131
137
|
self._ignore_templates = True
|
|
138
|
+
self._leave_no_match = False
|
|
139
|
+
self._no_match_value = None
|
|
140
|
+
self._stringer = str
|
|
141
|
+
self._unpack_string_joiner = ", "
|
|
132
142
|
return
|
|
133
143
|
|
|
144
|
+
def _get_value_regex_pattern(self, level: int = 0) -> _RegexPattern:
|
|
145
|
+
level_patterns = self._pattern_value.setdefault(level, {})
|
|
146
|
+
if level in level_patterns:
|
|
147
|
+
return level_patterns[level]
|
|
148
|
+
count = self._repeater_count_value + level
|
|
149
|
+
pattern = _RegexPattern(
|
|
150
|
+
start=f"{self._marker_start_value}{self._repeater_start_value * count} ",
|
|
151
|
+
end=f" {self._repeater_end_value * count}{self._marker_end_value}",
|
|
152
|
+
)
|
|
153
|
+
level_patterns[level] = pattern
|
|
154
|
+
return pattern
|
|
155
|
+
|
|
134
156
|
def fill(
|
|
135
157
|
self,
|
|
136
158
|
templated_data: dict | list | str,
|
|
137
159
|
source_data: dict | list,
|
|
138
160
|
current_path: str = "",
|
|
139
|
-
always_list: bool = True,
|
|
140
161
|
recursive: bool = True,
|
|
141
162
|
raise_no_match: bool = True,
|
|
163
|
+
leave_no_match: bool = False,
|
|
164
|
+
no_match_value: Any = None,
|
|
165
|
+
stringer: Callable[[str], str] = str,
|
|
166
|
+
unpack_string_joiner: str = ", ",
|
|
142
167
|
relative_template_keys: list[str] | None = None,
|
|
168
|
+
implicit_root: bool = True,
|
|
169
|
+
level: int = 0,
|
|
143
170
|
):
|
|
144
171
|
self._data = templated_data
|
|
145
172
|
self._source = source_data
|
|
146
173
|
self._recursive = recursive
|
|
147
174
|
self._raise_no_match = raise_no_match
|
|
175
|
+
self._leave_no_match = leave_no_match
|
|
176
|
+
self._no_match_value = no_match_value
|
|
177
|
+
self._stringer = stringer
|
|
178
|
+
self._unpack_string_joiner = unpack_string_joiner
|
|
179
|
+
self._add_prefix = implicit_root
|
|
148
180
|
self._template_keys = relative_template_keys or []
|
|
149
181
|
path = (f"$.{current_path}" if self._add_prefix else current_path) if current_path else "$"
|
|
150
182
|
if not relative_template_keys:
|
|
@@ -152,15 +184,15 @@ class TemplateFiller:
|
|
|
152
184
|
return self._recursive_subst(
|
|
153
185
|
templ=self._data,
|
|
154
186
|
current_path=path,
|
|
155
|
-
always_list=always_list,
|
|
156
187
|
relative_path_anchor=path,
|
|
188
|
+
level=level,
|
|
157
189
|
)
|
|
158
190
|
self._ignore_templates = True
|
|
159
191
|
first_pass = self._recursive_subst(
|
|
160
192
|
templ=self._data,
|
|
161
193
|
current_path=path,
|
|
162
|
-
always_list=always_list,
|
|
163
194
|
relative_path_anchor=path,
|
|
195
|
+
level=level,
|
|
164
196
|
)
|
|
165
197
|
if self._data is self._source:
|
|
166
198
|
self._source = first_pass
|
|
@@ -169,53 +201,28 @@ class TemplateFiller:
|
|
|
169
201
|
return self._recursive_subst(
|
|
170
202
|
templ=self._data,
|
|
171
203
|
current_path=path,
|
|
172
|
-
always_list=always_list,
|
|
173
204
|
relative_path_anchor=path,
|
|
205
|
+
level=level,
|
|
174
206
|
)
|
|
175
207
|
|
|
176
|
-
def _recursive_subst(self, templ, current_path: str,
|
|
177
|
-
|
|
178
|
-
def raise_error(
|
|
179
|
-
path_invalid: str,
|
|
180
|
-
description_template: str,
|
|
181
|
-
):
|
|
182
|
-
raise _exception.update.PySerialsUpdateTemplatedDataError(
|
|
183
|
-
description_template=description_template,
|
|
184
|
-
path_invalid=path_invalid,
|
|
185
|
-
path=current_path,
|
|
186
|
-
data=templ,
|
|
187
|
-
data_full=self._data,
|
|
188
|
-
data_source=self._source,
|
|
189
|
-
template_start=self._marker_start,
|
|
190
|
-
template_end=self._marker_end,
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
def _rec_match(expr):
|
|
208
|
+
def _recursive_subst(self, templ, current_path: str, relative_path_anchor: str, level: int):
|
|
194
209
|
|
|
195
|
-
|
|
210
|
+
def get_code_value(code_str: str):
|
|
211
|
+
code_lines = ["def __inline_code__():"]
|
|
212
|
+
code_lines.extend([f" {line}" for line in code_str.strip("\n").splitlines()])
|
|
213
|
+
code_str_full = "\n".join(code_lines)
|
|
214
|
+
global_context = {}
|
|
215
|
+
local_context = {}
|
|
216
|
+
try:
|
|
217
|
+
exec(code_str_full, global_context, local_context)
|
|
218
|
+
return local_context["__inline_code__"]()
|
|
219
|
+
except Exception as e:
|
|
196
220
|
raise_error(
|
|
197
|
-
path_invalid
|
|
198
|
-
|
|
221
|
+
description_template=f"Code at {{path_invalid}} raised an exception: {e}\n{code_str_full}",
|
|
222
|
+
path_invalid=current_path,
|
|
199
223
|
)
|
|
200
224
|
|
|
201
|
-
|
|
202
|
-
if matches:
|
|
203
|
-
return matches
|
|
204
|
-
if isinstance(expr.left, _jsonpath.Root):
|
|
205
|
-
raise_error_path_invalid()
|
|
206
|
-
whole_matches = []
|
|
207
|
-
left_matches = _rec_match(expr.left)
|
|
208
|
-
for left_match in left_matches:
|
|
209
|
-
left_match_filled = self._recursive_subst(
|
|
210
|
-
left_match.value, current_path=str(expr.left), always_list=False, relative_path_anchor=str(expr.left)
|
|
211
|
-
) if isinstance(left_match.value, str) else left_match.value
|
|
212
|
-
right_matches = expr.right.find(left_match_filled)
|
|
213
|
-
whole_matches.extend(right_matches)
|
|
214
|
-
if not whole_matches:
|
|
215
|
-
raise_error_path_invalid()
|
|
216
|
-
return whole_matches
|
|
217
|
-
|
|
218
|
-
def get_address_value(re_match):
|
|
225
|
+
def get_address_value(re_match, return_all_matches: bool = False):
|
|
219
226
|
path, num_periods = self._remove_leading_periods(re_match.group(1).strip())
|
|
220
227
|
if num_periods == 0:
|
|
221
228
|
path = f"$.{path}" if self._add_prefix else path
|
|
@@ -244,20 +251,26 @@ class TemplateFiller:
|
|
|
244
251
|
)
|
|
245
252
|
root_path_expr = root_path_expr.left
|
|
246
253
|
path_expr = root_path_expr.child(path_expr)
|
|
247
|
-
|
|
254
|
+
value, matched = get_value(path_expr, return_all_matches)
|
|
255
|
+
if matched:
|
|
256
|
+
return value
|
|
257
|
+
if self._leave_no_match:
|
|
258
|
+
return re_match.group()
|
|
259
|
+
return self._no_match_value
|
|
248
260
|
|
|
249
|
-
def get_value(jsonpath):
|
|
261
|
+
def get_value(jsonpath, return_all_matches: bool) -> tuple[Any, bool]:
|
|
250
262
|
matches = _rec_match(jsonpath)
|
|
263
|
+
if not matches:
|
|
264
|
+
if not return_all_matches and self._raise_no_match:
|
|
265
|
+
raise_error(
|
|
266
|
+
path_invalid=str(jsonpath),
|
|
267
|
+
description_template="JSONPath expression {path_invalid} did not match any data.",
|
|
268
|
+
)
|
|
269
|
+
return None, False
|
|
251
270
|
values = [m.value for m in matches]
|
|
252
|
-
if
|
|
253
|
-
raise_error(
|
|
254
|
-
path_invalid=str(jsonpath),
|
|
255
|
-
description_template="JSONPath expression {path_invalid} did not match any data.",
|
|
256
|
-
)
|
|
257
|
-
single = len(values) == 1 and not always_list
|
|
258
|
-
output = values[0] if single else values
|
|
271
|
+
output = values if return_all_matches or len(values) > 1 else values[0]
|
|
259
272
|
if not self._recursive:
|
|
260
|
-
return output
|
|
273
|
+
return output, True
|
|
261
274
|
if relative_path_anchor == current_path:
|
|
262
275
|
path_fields = self._extract_fields(jsonpath)
|
|
263
276
|
has_template_key = any(field in self._template_keys for field in path_fields)
|
|
@@ -267,45 +280,140 @@ class TemplateFiller:
|
|
|
267
280
|
return self._recursive_subst(
|
|
268
281
|
output,
|
|
269
282
|
current_path=str(jsonpath),
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
)
|
|
283
|
+
relative_path_anchor=_rel_path_anchor,
|
|
284
|
+
level=0,
|
|
285
|
+
), True
|
|
286
|
+
|
|
287
|
+
def _rec_match(expr) -> list:
|
|
288
|
+
matches = expr.find(self._source)
|
|
289
|
+
if matches:
|
|
290
|
+
return matches
|
|
291
|
+
if isinstance(expr.left, _jsonpath.Root):
|
|
292
|
+
return []
|
|
293
|
+
whole_matches = []
|
|
294
|
+
left_matches = _rec_match(expr.left)
|
|
295
|
+
for left_match in left_matches:
|
|
296
|
+
left_match_filled = self._recursive_subst(
|
|
297
|
+
templ=left_match.value,
|
|
298
|
+
current_path=str(expr.left),
|
|
299
|
+
relative_path_anchor=str(expr.left),
|
|
300
|
+
level=0,
|
|
301
|
+
) if isinstance(left_match.value, str) else left_match.value
|
|
302
|
+
right_matches = expr.right.find(left_match_filled)
|
|
303
|
+
whole_matches.extend(right_matches)
|
|
304
|
+
return whole_matches
|
|
273
305
|
|
|
274
306
|
def get_relative_path(new_path):
|
|
275
307
|
return new_path if current_path == relative_path_anchor else relative_path_anchor
|
|
276
308
|
|
|
309
|
+
def raise_error(
|
|
310
|
+
path_invalid: str,
|
|
311
|
+
description_template: str,
|
|
312
|
+
):
|
|
313
|
+
raise _exception.update.PySerialsUpdateTemplatedDataError(
|
|
314
|
+
description_template=description_template,
|
|
315
|
+
path_invalid=path_invalid,
|
|
316
|
+
path=current_path,
|
|
317
|
+
data=templ,
|
|
318
|
+
data_full=self._data,
|
|
319
|
+
data_source=self._source,
|
|
320
|
+
template_start=self._marker_start_value,
|
|
321
|
+
template_end=self._marker_end_value,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def string_filler_unpack(match: _re.Match):
|
|
325
|
+
match_list = self._pattern_list.fullmatch(match.group(1).strip())
|
|
326
|
+
if match_list:
|
|
327
|
+
values = get_address_value(match_list, return_all_matches=True)
|
|
328
|
+
else:
|
|
329
|
+
match_code = self._pattern_code.fullmatch(match.group(1).strip())
|
|
330
|
+
if match_code:
|
|
331
|
+
values = get_code_value(match_code.group(1))
|
|
332
|
+
else:
|
|
333
|
+
values = get_address_value(match)
|
|
334
|
+
return self._unpack_string_joiner.join([self._stringer(val) for val in values])
|
|
335
|
+
|
|
277
336
|
if isinstance(templ, str):
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
337
|
+
pattern_nested = self._get_value_regex_pattern(level=level + 1)
|
|
338
|
+
templ_nested_filled = pattern_nested.sub(
|
|
339
|
+
lambda x: self._recursive_subst(
|
|
340
|
+
templ=x.group(),
|
|
341
|
+
current_path=current_path,
|
|
342
|
+
relative_path_anchor=get_relative_path(current_path),
|
|
343
|
+
level=level+1,
|
|
344
|
+
),
|
|
283
345
|
templ
|
|
284
346
|
)
|
|
347
|
+
pattern_value = self._get_value_regex_pattern(level=level)
|
|
348
|
+
whole_match_value = pattern_value.fullmatch(templ_nested_filled)
|
|
349
|
+
if whole_match_value:
|
|
350
|
+
return get_address_value(whole_match_value)
|
|
351
|
+
templ_values_filled = pattern_value.sub(
|
|
352
|
+
lambda x: str(get_address_value(x)),
|
|
353
|
+
templ_nested_filled
|
|
354
|
+
)
|
|
355
|
+
whole_match_list = self._pattern_list.fullmatch(templ_values_filled.strip())
|
|
356
|
+
if whole_match_list:
|
|
357
|
+
return get_address_value(whole_match_list, return_all_matches=True)
|
|
358
|
+
whole_match_unpack = self._pattern_unpack.fullmatch(templ_values_filled.strip())
|
|
359
|
+
if whole_match_unpack:
|
|
360
|
+
submatch_list = self._pattern_list.fullmatch(whole_match_unpack.group(1).strip())
|
|
361
|
+
if submatch_list:
|
|
362
|
+
return get_address_value(submatch_list, return_all_matches=True)
|
|
363
|
+
submatch_code = self._pattern_code.fullmatch(whole_match_unpack.group(1).strip())
|
|
364
|
+
if submatch_code:
|
|
365
|
+
return get_code_value(submatch_code.group(1))
|
|
366
|
+
return get_address_value(whole_match_unpack)
|
|
367
|
+
whole_match_code = self._pattern_code.fullmatch(templ_values_filled.strip())
|
|
368
|
+
if whole_match_code:
|
|
369
|
+
templ_list_filled = self._pattern_list.sub(
|
|
370
|
+
lambda x: str(get_address_value(x, return_all_matches=True)),
|
|
371
|
+
whole_match_code.group(1)
|
|
372
|
+
)
|
|
373
|
+
return get_code_value(templ_list_filled)
|
|
374
|
+
unpacked_filled = self._pattern_unpack.sub(string_filler_unpack, templ_values_filled)
|
|
375
|
+
return self._pattern_code.sub(
|
|
376
|
+
lambda x: self._stringer(get_code_value(x.group(1))),
|
|
377
|
+
unpacked_filled
|
|
378
|
+
)
|
|
379
|
+
|
|
285
380
|
if isinstance(templ, list):
|
|
286
381
|
out = []
|
|
287
382
|
for idx, elem in enumerate(templ):
|
|
288
383
|
new_path = f"{current_path}[{idx}]"
|
|
289
384
|
elem_filled = self._recursive_subst(
|
|
290
|
-
templ=elem,
|
|
385
|
+
templ=elem,
|
|
386
|
+
current_path=new_path,
|
|
387
|
+
relative_path_anchor=get_relative_path(new_path),
|
|
388
|
+
level=0,
|
|
291
389
|
)
|
|
292
|
-
if isinstance(elem, str) and self.
|
|
390
|
+
if isinstance(elem, str) and self._pattern_unpack.fullmatch(elem.strip()):
|
|
293
391
|
out.extend(elem_filled)
|
|
294
392
|
else:
|
|
295
393
|
out.append(elem_filled)
|
|
296
394
|
return out
|
|
395
|
+
|
|
297
396
|
if isinstance(templ, dict):
|
|
298
397
|
new_dict = {}
|
|
299
398
|
for key, val in templ.items():
|
|
300
399
|
key_filled = self._recursive_subst(
|
|
301
|
-
templ=key,
|
|
400
|
+
templ=key,
|
|
401
|
+
current_path=current_path,
|
|
402
|
+
relative_path_anchor=relative_path_anchor,
|
|
403
|
+
level=0,
|
|
302
404
|
)
|
|
405
|
+
if isinstance(key, str) and self._pattern_unpack.fullmatch(key.strip()):
|
|
406
|
+
new_dict.update(key_filled)
|
|
407
|
+
continue
|
|
303
408
|
if key_filled in self._template_keys:
|
|
304
409
|
new_dict[key_filled] = val
|
|
305
410
|
continue
|
|
306
411
|
new_path = f"{current_path}.'{key_filled}'"
|
|
307
412
|
new_dict[key_filled] = self._recursive_subst(
|
|
308
|
-
templ=val,
|
|
413
|
+
templ=val,
|
|
414
|
+
current_path=new_path,
|
|
415
|
+
relative_path_anchor=get_relative_path(new_path),
|
|
416
|
+
level=0,
|
|
309
417
|
)
|
|
310
418
|
return new_dict
|
|
311
419
|
return templ
|
|
@@ -334,4 +442,24 @@ class TemplateFiller:
|
|
|
334
442
|
return
|
|
335
443
|
fields = []
|
|
336
444
|
_recursive_extract(jsonpath)
|
|
337
|
-
return fields
|
|
445
|
+
return fields
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
class _RegexPattern:
|
|
449
|
+
|
|
450
|
+
def __init__(self, start: str, end: str):
|
|
451
|
+
start_esc = _re.escape(start)
|
|
452
|
+
end_esc = _re.escape(end)
|
|
453
|
+
self.pattern = _re.compile(rf"{start_esc}(.*?)(?={end_esc}){end_esc}", re.DOTALL)
|
|
454
|
+
return
|
|
455
|
+
|
|
456
|
+
def fullmatch(self, string: str) -> _re.Match | None:
|
|
457
|
+
# Use findall to count occurrences of segments in the text
|
|
458
|
+
matches = self.pattern.findall(string)
|
|
459
|
+
if len(matches) == 1:
|
|
460
|
+
# Verify the match spans the entire string
|
|
461
|
+
return self.pattern.fullmatch(string)
|
|
462
|
+
return None
|
|
463
|
+
|
|
464
|
+
def sub(self, repl, string: str) -> str:
|
|
465
|
+
return self.pattern.sub(repl, string)
|
|
File without changes
|
|
File without changes
|