PySerials 0.0.0.dev31__tar.gz → 0.0.0.dev33__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.dev31 → pyserials-0.0.0.dev33}/PKG-INFO +3 -3
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/pyproject.toml +3 -3
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/PySerials.egg-info/PKG-INFO +3 -3
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/PySerials.egg-info/requires.txt +2 -2
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/nested_dict.py +19 -47
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/update.py +188 -142
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/README.md +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/setup.cfg +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/PySerials.egg-info/SOURCES.txt +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/PySerials.egg-info/dependency_links.txt +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/PySerials.egg-info/not-zip-safe +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/PySerials.egg-info/top_level.txt +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/__init__.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/compare.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/exception/__init__.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/exception/_base.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/exception/read.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/exception/update.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/exception/validate.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/format.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/read.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/validate.py +0 -0
- {pyserials-0.0.0.dev31 → pyserials-0.0.0.dev33}/src/pyserials/write.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.dev33
|
|
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.dev30
|
|
12
|
+
Requires-Dist: ExceptionMan==0.0.0.dev30
|
|
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.dev33"
|
|
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.dev30",
|
|
30
|
+
"ExceptionMan == 0.0.0.dev30",
|
|
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.dev33
|
|
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.dev30
|
|
12
|
+
Requires-Dist: ExceptionMan==0.0.0.dev30
|
|
13
13
|
Requires-Dist: ProtocolMan==0.0.0.dev2
|
|
@@ -24,14 +24,17 @@ class NestedDict:
|
|
|
24
24
|
end_list: str = "]]$",
|
|
25
25
|
end_unpack: str = "}}*",
|
|
26
26
|
end_code: str = "}}#",
|
|
27
|
-
recursive: bool = True,
|
|
28
27
|
raise_no_match: bool = True,
|
|
29
28
|
leave_no_match: bool = False,
|
|
30
29
|
no_match_value: Any = None,
|
|
30
|
+
code_context: dict[str, Any] | None = None,
|
|
31
|
+
code_context_partial: dict[str, Callable | tuple[Callable, str]] | None = None,
|
|
32
|
+
code_context_call: dict[str, Callable[[Callable], Any]] | None = None,
|
|
31
33
|
stringer: Callable[[str], str] = str,
|
|
32
34
|
unpack_string_joiner: str = ", ",
|
|
33
35
|
relative_template_keys: list[str] | None = None,
|
|
34
36
|
implicit_root: bool = True,
|
|
37
|
+
getter_function_name: str = "get",
|
|
35
38
|
):
|
|
36
39
|
self._data = data or {}
|
|
37
40
|
self._templater = _ps.update.TemplateFiller(
|
|
@@ -46,27 +49,21 @@ class NestedDict:
|
|
|
46
49
|
end_list=end_list,
|
|
47
50
|
end_unpack=end_unpack,
|
|
48
51
|
end_code=end_code,
|
|
52
|
+
raise_no_match=raise_no_match,
|
|
53
|
+
leave_no_match=leave_no_match,
|
|
54
|
+
no_match_value=no_match_value,
|
|
55
|
+
code_context=code_context,
|
|
56
|
+
code_context_partial=code_context_partial,
|
|
57
|
+
code_context_call=code_context_call,
|
|
58
|
+
stringer=stringer,
|
|
59
|
+
unpack_string_joiner=unpack_string_joiner,
|
|
60
|
+
relative_template_keys=relative_template_keys,
|
|
61
|
+
implicit_root=implicit_root,
|
|
62
|
+
getter_function_name=getter_function_name,
|
|
49
63
|
)
|
|
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
|
|
58
64
|
return
|
|
59
65
|
|
|
60
|
-
def fill(
|
|
61
|
-
self,
|
|
62
|
-
path: str = "",
|
|
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,
|
|
69
|
-
):
|
|
66
|
+
def fill(self, path: str = ""):
|
|
70
67
|
if not path:
|
|
71
68
|
value = self._data
|
|
72
69
|
else:
|
|
@@ -76,12 +73,6 @@ class NestedDict:
|
|
|
76
73
|
filled_value = self.fill_data(
|
|
77
74
|
data=value,
|
|
78
75
|
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,
|
|
85
76
|
)
|
|
86
77
|
if not path:
|
|
87
78
|
self._data = filled_value
|
|
@@ -89,30 +80,11 @@ class NestedDict:
|
|
|
89
80
|
self.__setitem__(path, filled_value)
|
|
90
81
|
return filled_value
|
|
91
82
|
|
|
92
|
-
def fill_data(
|
|
93
|
-
self,
|
|
94
|
-
data,
|
|
95
|
-
current_path: str = "",
|
|
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,
|
|
102
|
-
):
|
|
83
|
+
def fill_data(self, data, current_path: str = ""):
|
|
103
84
|
return self._templater.fill(
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
data=self._data,
|
|
86
|
+
template=data,
|
|
106
87
|
current_path=current_path,
|
|
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,
|
|
113
|
-
relative_template_keys=self._relative_template_keys,
|
|
114
|
-
implicit_root=self._implicit_root,
|
|
115
|
-
level=level,
|
|
116
88
|
)
|
|
117
89
|
|
|
118
90
|
def __call__(self):
|
|
@@ -3,6 +3,7 @@ from __future__ import annotations as _annotations
|
|
|
3
3
|
import re
|
|
4
4
|
from typing import TYPE_CHECKING as _TYPE_CHECKING
|
|
5
5
|
import re as _re
|
|
6
|
+
from functools import partial as _partial
|
|
6
7
|
|
|
7
8
|
import jsonpath_ng as _jsonpath
|
|
8
9
|
from jsonpath_ng import exceptions as _jsonpath_exceptions
|
|
@@ -116,6 +117,17 @@ class TemplateFiller:
|
|
|
116
117
|
end_list: str = "]]$",
|
|
117
118
|
end_unpack: str = "}}*",
|
|
118
119
|
end_code: str = "}}#",
|
|
120
|
+
raise_no_match: bool = True,
|
|
121
|
+
leave_no_match: bool = False,
|
|
122
|
+
no_match_value: Any = None,
|
|
123
|
+
code_context: dict[str, Any] | None = None,
|
|
124
|
+
code_context_partial: dict[str, Callable | tuple[Callable, str]] | None = None,
|
|
125
|
+
code_context_call: dict[str, Callable[[Callable], Any]] | None = None,
|
|
126
|
+
stringer: Callable[[str], str] = str,
|
|
127
|
+
unpack_string_joiner: str = ", ",
|
|
128
|
+
relative_template_keys: list[str] | None = None,
|
|
129
|
+
implicit_root: bool = True,
|
|
130
|
+
getter_function_name: str = "get",
|
|
119
131
|
):
|
|
120
132
|
self._marker_start_value = marker_start_value
|
|
121
133
|
self._marker_end_value = marker_end_value
|
|
@@ -125,93 +137,64 @@ class TemplateFiller:
|
|
|
125
137
|
self._pattern_list = _RegexPattern(start=start_list, end=end_list)
|
|
126
138
|
self._pattern_unpack = _RegexPattern(start=start_unpack, end=end_unpack)
|
|
127
139
|
self._pattern_code = _RegexPattern(start=start_code, end=end_code)
|
|
128
|
-
|
|
140
|
+
|
|
141
|
+
self._raise_no_match = raise_no_match
|
|
142
|
+
self._leave_no_match = leave_no_match
|
|
143
|
+
self._no_match_value = no_match_value
|
|
144
|
+
self._code_context = code_context or {}
|
|
145
|
+
self._code_context_partial = code_context_partial or {}
|
|
146
|
+
self._code_context_call = code_context_call or {}
|
|
147
|
+
self._stringer = stringer
|
|
148
|
+
self._unpack_string_joiner = unpack_string_joiner
|
|
149
|
+
self._add_prefix = implicit_root
|
|
150
|
+
self._template_keys = relative_template_keys or []
|
|
151
|
+
self._getter_function_name = getter_function_name
|
|
129
152
|
|
|
130
153
|
self._pattern_value: dict[int, _RegexPattern] = {}
|
|
131
154
|
self._data = None
|
|
132
|
-
self._source = None
|
|
133
|
-
self._recursive = None
|
|
134
|
-
self._path = None
|
|
135
|
-
self._raise_no_match = None
|
|
136
|
-
self._template_keys = None
|
|
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 = ", "
|
|
142
155
|
return
|
|
143
156
|
|
|
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
|
-
|
|
156
157
|
def fill(
|
|
157
158
|
self,
|
|
158
|
-
|
|
159
|
-
|
|
159
|
+
data: dict | list,
|
|
160
|
+
template: dict | list | str | None = None,
|
|
160
161
|
current_path: str = "",
|
|
161
|
-
recursive: bool = True,
|
|
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 = ", ",
|
|
167
|
-
relative_template_keys: list[str] | None = None,
|
|
168
|
-
implicit_root: bool = True,
|
|
169
|
-
level: int = 0,
|
|
170
162
|
):
|
|
171
|
-
self._data =
|
|
172
|
-
self._source = source_data
|
|
173
|
-
self._recursive = recursive
|
|
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
|
|
180
|
-
self._template_keys = relative_template_keys or []
|
|
163
|
+
self._data = data
|
|
181
164
|
path = (f"$.{current_path}" if self._add_prefix else current_path) if current_path else "$"
|
|
182
|
-
if not relative_template_keys:
|
|
183
|
-
self._ignore_templates = False
|
|
184
|
-
return self._recursive_subst(
|
|
185
|
-
templ=self._data,
|
|
186
|
-
current_path=path,
|
|
187
|
-
relative_path_anchor=path,
|
|
188
|
-
level=level,
|
|
189
|
-
)
|
|
190
|
-
self._ignore_templates = True
|
|
191
|
-
first_pass = self._recursive_subst(
|
|
192
|
-
templ=self._data,
|
|
193
|
-
current_path=path,
|
|
194
|
-
relative_path_anchor=path,
|
|
195
|
-
level=level,
|
|
196
|
-
)
|
|
197
|
-
if self._data is self._source:
|
|
198
|
-
self._source = first_pass
|
|
199
|
-
self._data = first_pass
|
|
200
|
-
self._ignore_templates = False
|
|
201
165
|
return self._recursive_subst(
|
|
202
|
-
templ=
|
|
166
|
+
templ=template or data,
|
|
203
167
|
current_path=path,
|
|
204
168
|
relative_path_anchor=path,
|
|
205
|
-
level=
|
|
169
|
+
level=0,
|
|
170
|
+
current_chain=[path],
|
|
206
171
|
)
|
|
207
172
|
|
|
208
|
-
def _recursive_subst(self, templ, current_path: str, relative_path_anchor: str, level: int):
|
|
173
|
+
def _recursive_subst(self, templ, current_path: str, relative_path_anchor: str, level: int, current_chain: list[str]):
|
|
174
|
+
|
|
175
|
+
def get_code_value(match: _re.Match | str):
|
|
209
176
|
|
|
210
|
-
|
|
177
|
+
def getter_function(path: str, default: Any = None, search: bool = False):
|
|
178
|
+
value, matched = get_address_value(path, return_all_matches=search, from_code=True)
|
|
179
|
+
if matched:
|
|
180
|
+
return value
|
|
181
|
+
if search:
|
|
182
|
+
return []
|
|
183
|
+
return default
|
|
184
|
+
|
|
185
|
+
code_str = match if isinstance(match, str) else match.group(1)
|
|
211
186
|
code_lines = ["def __inline_code__():"]
|
|
212
187
|
code_lines.extend([f" {line}" for line in code_str.strip("\n").splitlines()])
|
|
213
188
|
code_str_full = "\n".join(code_lines)
|
|
214
|
-
global_context = {}
|
|
189
|
+
global_context = self._code_context.copy() | {self._getter_function_name: getter_function}
|
|
190
|
+
for name, partial_func_data in self._code_context_partial.items():
|
|
191
|
+
if isinstance(partial_func_data, tuple):
|
|
192
|
+
func, arg_name = partial_func_data
|
|
193
|
+
global_context[name] = _partial(func, **{arg_name: getter_function})
|
|
194
|
+
else:
|
|
195
|
+
global_context[name] = _partial(partial_func_data, getter_function)
|
|
196
|
+
for name, call_func in self._code_context_call.items():
|
|
197
|
+
global_context[name] = call_func(getter_function)
|
|
215
198
|
local_context = {}
|
|
216
199
|
try:
|
|
217
200
|
exec(code_str_full, global_context, local_context)
|
|
@@ -222,8 +205,9 @@ class TemplateFiller:
|
|
|
222
205
|
path_invalid=current_path,
|
|
223
206
|
)
|
|
224
207
|
|
|
225
|
-
def get_address_value(
|
|
226
|
-
|
|
208
|
+
def get_address_value(match: _re.Match | str, return_all_matches: bool = False, from_code: bool = False):
|
|
209
|
+
raw_path = match if isinstance(match, str) else str(match.group(1))
|
|
210
|
+
path, num_periods = self._remove_leading_periods(raw_path.strip())
|
|
227
211
|
if num_periods == 0:
|
|
228
212
|
path = f"$.{path}" if self._add_prefix else path
|
|
229
213
|
try:
|
|
@@ -233,13 +217,14 @@ class TemplateFiller:
|
|
|
233
217
|
path_invalid=path,
|
|
234
218
|
description_template="JSONPath expression {path_invalid} is invalid.",
|
|
235
219
|
)
|
|
236
|
-
if self._ignore_templates:
|
|
237
|
-
path_fields = self._extract_fields(path_expr)
|
|
238
|
-
has_template_key = any(field in self._template_keys for field in path_fields)
|
|
239
|
-
if has_template_key:
|
|
240
|
-
return re_match.string
|
|
241
220
|
if num_periods:
|
|
242
|
-
|
|
221
|
+
if relative_path_anchor != current_path:
|
|
222
|
+
path_fields = self._extract_fields(_jsonpath.parse(current_path))
|
|
223
|
+
has_template_key = any(field in self._template_keys for field in path_fields)
|
|
224
|
+
anchor_path = relative_path_anchor if has_template_key else current_path
|
|
225
|
+
else:
|
|
226
|
+
anchor_path = current_path
|
|
227
|
+
root_path_expr = _jsonpath.parse(anchor_path)
|
|
243
228
|
for period in range(num_periods):
|
|
244
229
|
if isinstance(root_path_expr, _jsonpath.Root):
|
|
245
230
|
raise_error(
|
|
@@ -250,18 +235,24 @@ class TemplateFiller:
|
|
|
250
235
|
),
|
|
251
236
|
)
|
|
252
237
|
root_path_expr = root_path_expr.left
|
|
253
|
-
path_expr =
|
|
254
|
-
value, matched = get_value(path_expr, return_all_matches)
|
|
238
|
+
path_expr = self._concat_json_paths(root_path_expr, path_expr)
|
|
239
|
+
value, matched = get_value(path_expr, return_all_matches, from_code)
|
|
240
|
+
if from_code:
|
|
241
|
+
return value, matched
|
|
255
242
|
if matched:
|
|
256
243
|
return value
|
|
257
244
|
if self._leave_no_match:
|
|
258
|
-
return
|
|
245
|
+
return match.group()
|
|
259
246
|
return self._no_match_value
|
|
260
247
|
|
|
261
|
-
def get_value(jsonpath, return_all_matches: bool) -> tuple[Any, bool]:
|
|
248
|
+
def get_value(jsonpath, return_all_matches: bool, from_code: bool) -> tuple[Any, bool]:
|
|
262
249
|
matches = _rec_match(jsonpath)
|
|
263
250
|
if not matches:
|
|
264
|
-
if
|
|
251
|
+
if from_code:
|
|
252
|
+
return None, False
|
|
253
|
+
if return_all_matches:
|
|
254
|
+
return [], True
|
|
255
|
+
if self._raise_no_match:
|
|
265
256
|
raise_error(
|
|
266
257
|
path_invalid=str(jsonpath),
|
|
267
258
|
description_template="JSONPath expression {path_invalid} did not match any data.",
|
|
@@ -269,8 +260,6 @@ class TemplateFiller:
|
|
|
269
260
|
return None, False
|
|
270
261
|
values = [m.value for m in matches]
|
|
271
262
|
output = values if return_all_matches or len(values) > 1 else values[0]
|
|
272
|
-
if not self._recursive:
|
|
273
|
-
return output, True
|
|
274
263
|
if relative_path_anchor == current_path:
|
|
275
264
|
path_fields = self._extract_fields(jsonpath)
|
|
276
265
|
has_template_key = any(field in self._template_keys for field in path_fields)
|
|
@@ -282,10 +271,11 @@ class TemplateFiller:
|
|
|
282
271
|
current_path=str(jsonpath),
|
|
283
272
|
relative_path_anchor=_rel_path_anchor,
|
|
284
273
|
level=0,
|
|
274
|
+
current_chain=current_chain + [str(jsonpath)],
|
|
285
275
|
), True
|
|
286
276
|
|
|
287
277
|
def _rec_match(expr) -> list:
|
|
288
|
-
matches = expr.find(self.
|
|
278
|
+
matches = expr.find(self._data)
|
|
289
279
|
if matches:
|
|
290
280
|
return matches
|
|
291
281
|
if isinstance(expr.left, _jsonpath.Root):
|
|
@@ -298,6 +288,7 @@ class TemplateFiller:
|
|
|
298
288
|
current_path=str(expr.left),
|
|
299
289
|
relative_path_anchor=str(expr.left),
|
|
300
290
|
level=0,
|
|
291
|
+
current_chain=current_chain + [str(expr.left)],
|
|
301
292
|
) if isinstance(left_match.value, str) else left_match.value
|
|
302
293
|
right_matches = expr.right.find(left_match_filled)
|
|
303
294
|
whole_matches.extend(right_matches)
|
|
@@ -306,76 +297,102 @@ class TemplateFiller:
|
|
|
306
297
|
def get_relative_path(new_path):
|
|
307
298
|
return new_path if current_path == relative_path_anchor else relative_path_anchor
|
|
308
299
|
|
|
309
|
-
def
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
300
|
+
def fill_nested_values(match: _re.Match | str):
|
|
301
|
+
pattern_nested = self._get_value_regex_pattern(level=level + 1)
|
|
302
|
+
return pattern_nested.sub(
|
|
303
|
+
lambda x: self._recursive_subst(
|
|
304
|
+
templ=x.group(),
|
|
305
|
+
current_path=current_path,
|
|
306
|
+
relative_path_anchor=get_relative_path(current_path),
|
|
307
|
+
level=level + 1,
|
|
308
|
+
current_chain=current_chain,
|
|
309
|
+
),
|
|
310
|
+
match if isinstance(match, str) else match.group(1),
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def string_filler_unpack(match: _re.Match):
|
|
314
|
+
path = str(match.group(1)).strip()
|
|
315
|
+
match_list = self._pattern_list.fullmatch(path)
|
|
316
|
+
if match_list:
|
|
317
|
+
values = get_address_value(match_list, return_all_matches=True)
|
|
318
|
+
else:
|
|
319
|
+
match_code = self._pattern_code.fullmatch(path)
|
|
320
|
+
if match_code:
|
|
321
|
+
values = get_code_value(match_code)
|
|
322
|
+
else:
|
|
323
|
+
values = get_address_value(path)
|
|
324
|
+
return self._unpack_string_joiner.join([self._stringer(val) for val in values])
|
|
325
|
+
|
|
326
|
+
def raise_error(path_invalid: str, description_template: str):
|
|
313
327
|
raise _exception.update.PySerialsUpdateTemplatedDataError(
|
|
314
328
|
description_template=description_template,
|
|
315
329
|
path_invalid=path_invalid,
|
|
316
330
|
path=current_path,
|
|
317
331
|
data=templ,
|
|
318
332
|
data_full=self._data,
|
|
319
|
-
data_source=self.
|
|
333
|
+
data_source=self._data,
|
|
320
334
|
template_start=self._marker_start_value,
|
|
321
335
|
template_end=self._marker_end_value,
|
|
322
336
|
)
|
|
323
337
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
338
|
+
# if not internal:
|
|
339
|
+
# self._path_history.append(current_path)
|
|
340
|
+
# loop = self._find_loop()
|
|
341
|
+
# if loop:
|
|
342
|
+
# loop_str = "\n".join([f"- {path.replace("'", "")}" for path in loop])
|
|
343
|
+
# raise _exception.update.PySerialsUpdateTemplatedDataError(
|
|
344
|
+
# description_template=f"Path {{path_invalid}} starts a loop: {loop_str}",
|
|
345
|
+
# path_invalid=loop[0],
|
|
346
|
+
# path=current_path,
|
|
347
|
+
# data=templ,
|
|
348
|
+
# data_full=self._data,
|
|
349
|
+
# data_source=self._source,
|
|
350
|
+
# template_start=self._marker_start_value,
|
|
351
|
+
# template_end=self._marker_end_value,
|
|
352
|
+
# )
|
|
335
353
|
|
|
336
354
|
if isinstance(templ, str):
|
|
337
|
-
|
|
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
|
-
),
|
|
345
|
-
templ
|
|
346
|
-
)
|
|
355
|
+
# Handle value blocks
|
|
347
356
|
pattern_value = self._get_value_regex_pattern(level=level)
|
|
348
|
-
|
|
349
|
-
if
|
|
350
|
-
return get_address_value(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
submatch_code = self._pattern_code.fullmatch(
|
|
357
|
+
match_value = pattern_value.fullmatch(templ)
|
|
358
|
+
if match_value:
|
|
359
|
+
return get_address_value(fill_nested_values(match_value))
|
|
360
|
+
# Handle list blocks
|
|
361
|
+
match_list = self._pattern_list.fullmatch(templ)
|
|
362
|
+
if match_list:
|
|
363
|
+
return get_address_value(fill_nested_values(match_list), return_all_matches=True)
|
|
364
|
+
# Handle code blocks
|
|
365
|
+
match_code = self._pattern_code.fullmatch(templ)
|
|
366
|
+
if match_code:
|
|
367
|
+
return get_code_value(match_code)
|
|
368
|
+
# Handle unpack blocks
|
|
369
|
+
match_unpack = self._pattern_unpack.fullmatch(templ)
|
|
370
|
+
if match_unpack:
|
|
371
|
+
unpack_value = match_unpack.group(1)
|
|
372
|
+
submatch_code = self._pattern_code.fullmatch(unpack_value)
|
|
364
373
|
if submatch_code:
|
|
365
|
-
return get_code_value(submatch_code
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
374
|
+
return get_code_value(submatch_code)
|
|
375
|
+
unpack_value = fill_nested_values(unpack_value)
|
|
376
|
+
submatch_list = self._pattern_list.fullmatch(unpack_value)
|
|
377
|
+
if submatch_list:
|
|
378
|
+
return get_address_value(unpack_value, return_all_matches=True)
|
|
379
|
+
return get_address_value(unpack_value)
|
|
380
|
+
# Handle strings
|
|
381
|
+
code_blocks_filled = self._pattern_code.sub(
|
|
382
|
+
lambda x: self._stringer(get_code_value(x)),
|
|
383
|
+
templ
|
|
384
|
+
)
|
|
385
|
+
nested_values_filled = fill_nested_values(code_blocks_filled)
|
|
386
|
+
unpacked_filled = self._pattern_unpack.sub(string_filler_unpack, nested_values_filled)
|
|
387
|
+
lists_filled = self._pattern_list.sub(
|
|
388
|
+
lambda x: self._stringer(get_address_value(x)),
|
|
377
389
|
unpacked_filled
|
|
378
390
|
)
|
|
391
|
+
templ_values_filled = pattern_value.sub(
|
|
392
|
+
lambda x: self._stringer(get_address_value(x)),
|
|
393
|
+
lists_filled
|
|
394
|
+
)
|
|
395
|
+
return templ_values_filled
|
|
379
396
|
|
|
380
397
|
if isinstance(templ, list):
|
|
381
398
|
out = []
|
|
@@ -386,8 +403,9 @@ class TemplateFiller:
|
|
|
386
403
|
current_path=new_path,
|
|
387
404
|
relative_path_anchor=get_relative_path(new_path),
|
|
388
405
|
level=0,
|
|
406
|
+
current_chain=current_chain + [new_path],
|
|
389
407
|
)
|
|
390
|
-
if isinstance(elem, str) and self._pattern_unpack.fullmatch(elem
|
|
408
|
+
if isinstance(elem, str) and self._pattern_unpack.fullmatch(elem):
|
|
391
409
|
out.extend(elem_filled)
|
|
392
410
|
else:
|
|
393
411
|
out.append(elem_filled)
|
|
@@ -401,8 +419,9 @@ class TemplateFiller:
|
|
|
401
419
|
current_path=current_path,
|
|
402
420
|
relative_path_anchor=relative_path_anchor,
|
|
403
421
|
level=0,
|
|
422
|
+
current_chain=current_chain,
|
|
404
423
|
)
|
|
405
|
-
if isinstance(key, str) and self._pattern_unpack.fullmatch(key
|
|
424
|
+
if isinstance(key, str) and self._pattern_unpack.fullmatch(key):
|
|
406
425
|
new_dict.update(key_filled)
|
|
407
426
|
continue
|
|
408
427
|
if key_filled in self._template_keys:
|
|
@@ -414,10 +433,33 @@ class TemplateFiller:
|
|
|
414
433
|
current_path=new_path,
|
|
415
434
|
relative_path_anchor=get_relative_path(new_path),
|
|
416
435
|
level=0,
|
|
436
|
+
current_chain=current_chain + [new_path],
|
|
417
437
|
)
|
|
418
438
|
return new_dict
|
|
419
439
|
return templ
|
|
420
440
|
|
|
441
|
+
# def _find_loop(self):
|
|
442
|
+
# for pattern_length in range(1, len(self._path_history) // 2 + 1):
|
|
443
|
+
# # Slice the end of the list into two consecutive patterns
|
|
444
|
+
# pattern = self._path_history[-pattern_length:]
|
|
445
|
+
# previous_pattern = self._path_history[-2 * pattern_length:-pattern_length]
|
|
446
|
+
# # Check if the two patterns are the same
|
|
447
|
+
# if pattern == previous_pattern:
|
|
448
|
+
# pattern.insert(0, pattern[-1])
|
|
449
|
+
# return pattern
|
|
450
|
+
# return
|
|
451
|
+
|
|
452
|
+
def _get_value_regex_pattern(self, level: int = 0) -> _RegexPattern:
|
|
453
|
+
if level in self._pattern_value:
|
|
454
|
+
return self._pattern_value[level]
|
|
455
|
+
count = self._repeater_count_value + level
|
|
456
|
+
pattern = _RegexPattern(
|
|
457
|
+
start=f"{self._marker_start_value}{self._repeater_start_value * count} ",
|
|
458
|
+
end=f" {self._repeater_end_value * count}{self._marker_end_value}",
|
|
459
|
+
)
|
|
460
|
+
self._pattern_value[level] = pattern
|
|
461
|
+
return pattern
|
|
462
|
+
|
|
421
463
|
@staticmethod
|
|
422
464
|
def _remove_leading_periods(s: str) -> (str, int):
|
|
423
465
|
match = _re.match(r"^(\.*)(.*)", s)
|
|
@@ -444,6 +486,10 @@ class TemplateFiller:
|
|
|
444
486
|
_recursive_extract(jsonpath)
|
|
445
487
|
return fields
|
|
446
488
|
|
|
489
|
+
def _concat_json_paths(self, path1, path2):
|
|
490
|
+
if not isinstance(path2, _jsonpath.Child):
|
|
491
|
+
return _jsonpath.Child(path1, path2)
|
|
492
|
+
return _jsonpath.Child(self._concat_json_paths(path1, path2.left), path2.right)
|
|
447
493
|
|
|
448
494
|
class _RegexPattern:
|
|
449
495
|
|
|
@@ -458,8 +504,8 @@ class _RegexPattern:
|
|
|
458
504
|
matches = self.pattern.findall(string)
|
|
459
505
|
if len(matches) == 1:
|
|
460
506
|
# Verify the match spans the entire string
|
|
461
|
-
return self.pattern.fullmatch(string)
|
|
507
|
+
return self.pattern.fullmatch(string.strip())
|
|
462
508
|
return None
|
|
463
509
|
|
|
464
510
|
def sub(self, repl, string: str) -> str:
|
|
465
|
-
return self.pattern.sub(repl, string)
|
|
511
|
+
return self.pattern.sub(repl, string)
|
|
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
|