PySerials 0.0.0.dev27__tar.gz → 0.0.0.dev29__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.
Files changed (23) hide show
  1. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/PKG-INFO +3 -3
  2. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/pyproject.toml +3 -3
  3. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/PySerials.egg-info/PKG-INFO +3 -3
  4. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/PySerials.egg-info/requires.txt +2 -2
  5. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/nested_dict.py +4 -4
  6. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/update.py +86 -15
  7. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/README.md +0 -0
  8. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/setup.cfg +0 -0
  9. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/PySerials.egg-info/SOURCES.txt +0 -0
  10. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/PySerials.egg-info/dependency_links.txt +0 -0
  11. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/PySerials.egg-info/not-zip-safe +0 -0
  12. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/PySerials.egg-info/top_level.txt +0 -0
  13. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/__init__.py +0 -0
  14. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/compare.py +0 -0
  15. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/exception/__init__.py +0 -0
  16. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/exception/_base.py +0 -0
  17. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/exception/read.py +0 -0
  18. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/exception/update.py +0 -0
  19. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/exception/validate.py +0 -0
  20. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/format.py +0 -0
  21. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/read.py +0 -0
  22. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/src/pyserials/validate.py +0 -0
  23. {pyserials-0.0.0.dev27 → pyserials-0.0.0.dev29}/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.dev27
3
+ Version: 0.0.0.dev29
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.dev24
12
- Requires-Dist: ExceptionMan==0.0.0.dev24
11
+ Requires-Dist: MDit==0.0.0.dev26
12
+ Requires-Dist: ExceptionMan==0.0.0.dev26
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.dev27"
20
+ version = "0.0.0.dev29"
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.dev24",
30
- "ExceptionMan == 0.0.0.dev24",
29
+ "MDit == 0.0.0.dev26",
30
+ "ExceptionMan == 0.0.0.dev26",
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.dev27
3
+ Version: 0.0.0.dev29
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.dev24
12
- Requires-Dist: ExceptionMan==0.0.0.dev24
11
+ Requires-Dist: MDit==0.0.0.dev26
12
+ Requires-Dist: ExceptionMan==0.0.0.dev26
13
13
  Requires-Dist: ProtocolMan==0.0.0.dev2
@@ -4,6 +4,6 @@ jsonpath-ng<2,>=1.6.1
4
4
  ruamel.yaml<0.18,>=0.17.32
5
5
  ruamel.yaml.string<1,>=0.1.1
6
6
  tomlkit<0.12,>=0.11.8
7
- MDit==0.0.0.dev24
8
- ExceptionMan==0.0.0.dev24
7
+ MDit==0.0.0.dev26
8
+ ExceptionMan==0.0.0.dev26
9
9
  ProtocolMan==0.0.0.dev2
@@ -18,8 +18,8 @@ class NestedDict:
18
18
  template_marker_unpack_start: str = "*{{",
19
19
  template_marker_unpack_end: str = "}}",
20
20
  template_implicit_root: bool = True,
21
- template_stringer: Callable[[str], str] = None,
22
- template_ignore_key_regex: str | None = None,
21
+ template_stringer: Callable[[str], str] = str,
22
+ relative_template_keys: list[str] | None = None,
23
23
  ):
24
24
  self._data = data or {}
25
25
  self._templater = _ps.update.TemplateFiller(
@@ -30,7 +30,7 @@ class NestedDict:
30
30
  implicit_root=template_implicit_root,
31
31
  stringer=template_stringer,
32
32
  )
33
- self._ignore_key_regex = template_ignore_key_regex
33
+ self._relative_template_keys = relative_template_keys
34
34
  return
35
35
 
36
36
  def fill(
@@ -67,7 +67,7 @@ class NestedDict:
67
67
  current_path=current_path,
68
68
  always_list=always_list,
69
69
  recursive=recursive,
70
- ignore_key_regex=self._ignore_key_regex,
70
+ relative_template_keys=self._relative_template_keys,
71
71
  )
72
72
 
73
73
  def __call__(self):
@@ -8,7 +8,7 @@ from jsonpath_ng import exceptions as _jsonpath_exceptions
8
8
  import pyserials.exception as _exception
9
9
 
10
10
  if _TYPE_CHECKING:
11
- from typing import Literal, Callable
11
+ from typing import Literal, Callable, Sequence
12
12
 
13
13
 
14
14
  def dict_from_addon(
@@ -87,6 +87,18 @@ def data_from_jsonschema(data: dict | list, schema: dict) -> None:
87
87
  return
88
88
 
89
89
 
90
+ def remove_keys(data: dict | list, keys: str | Sequence[str]):
91
+ def recursive_pop(d):
92
+ if isinstance(d, dict):
93
+ return {k: recursive_pop(v) for k, v in d.items() if k not in keys}
94
+ if isinstance(d, list):
95
+ return [recursive_pop(v) for v in d]
96
+ return d
97
+ if isinstance(keys, str):
98
+ keys = [keys]
99
+ return recursive_pop(data)
100
+
101
+
90
102
  class TemplateFiller:
91
103
 
92
104
  def __init__(
@@ -115,7 +127,8 @@ class TemplateFiller:
115
127
  self._recursive = None
116
128
  self._path = None
117
129
  self._raise_no_match = None
118
- self._ignore_key_regex = None
130
+ self._template_keys = None
131
+ self._ignore_templates = True
119
132
  return
120
133
 
121
134
  def fill(
@@ -126,20 +139,41 @@ class TemplateFiller:
126
139
  always_list: bool = True,
127
140
  recursive: bool = True,
128
141
  raise_no_match: bool = True,
129
- ignore_key_regex: str | None = None,
142
+ relative_template_keys: list[str] | None = None,
130
143
  ):
131
144
  self._data = templated_data
132
145
  self._source = source_data
133
146
  self._recursive = recursive
134
147
  self._raise_no_match = raise_no_match
135
- self._ignore_key_regex = ignore_key_regex
148
+ self._template_keys = relative_template_keys or []
149
+ path = (f"$.{current_path}" if self._add_prefix else current_path) if current_path else "$"
150
+ if not relative_template_keys:
151
+ self._ignore_templates = False
152
+ return self._recursive_subst(
153
+ templ=self._data,
154
+ current_path=path,
155
+ always_list=always_list,
156
+ relative_path_anchor=path,
157
+ )
158
+ self._ignore_templates = True
159
+ first_pass = self._recursive_subst(
160
+ templ=self._data,
161
+ current_path=path,
162
+ always_list=always_list,
163
+ relative_path_anchor=path,
164
+ )
165
+ if self._data is self._source:
166
+ self._source = first_pass
167
+ self._data = first_pass
168
+ self._ignore_templates = False
136
169
  return self._recursive_subst(
137
170
  templ=self._data,
138
- current_path=(f"$.{current_path}" if self._add_prefix else current_path) if current_path else "$",
139
- always_list=always_list
171
+ current_path=path,
172
+ always_list=always_list,
173
+ relative_path_anchor=path,
140
174
  )
141
175
 
142
- def _recursive_subst(self, templ, current_path: str, always_list: bool):
176
+ def _recursive_subst(self, templ, current_path: str, always_list: bool, relative_path_anchor: str):
143
177
 
144
178
  def raise_error(
145
179
  path_invalid: str,
@@ -173,7 +207,7 @@ class TemplateFiller:
173
207
  left_matches = _rec_match(expr.left)
174
208
  for left_match in left_matches:
175
209
  left_match_filled = self._recursive_subst(
176
- left_match.value, current_path=str(expr.left), always_list=False
210
+ left_match.value, current_path=str(expr.left), always_list=False, relative_path_anchor=str(expr.left)
177
211
  ) if isinstance(left_match.value, str) else left_match.value
178
212
  right_matches = expr.right.find(left_match_filled)
179
213
  whole_matches.extend(right_matches)
@@ -192,8 +226,13 @@ class TemplateFiller:
192
226
  path_invalid=path,
193
227
  description_template="JSONPath expression {path_invalid} is invalid.",
194
228
  )
229
+ if self._ignore_templates:
230
+ path_fields = self._extract_fields(path_expr)
231
+ has_template_key = any(field in self._template_keys for field in path_fields)
232
+ if has_template_key:
233
+ return re_match.string
195
234
  if num_periods:
196
- root_path_expr = _jsonpath.parse(current_path)
235
+ root_path_expr = _jsonpath.parse(relative_path_anchor)
197
236
  for period in range(num_periods):
198
237
  if isinstance(root_path_expr, _jsonpath.Root):
199
238
  raise_error(
@@ -219,10 +258,24 @@ class TemplateFiller:
219
258
  output = values[0] if single else values
220
259
  if not self._recursive:
221
260
  return output
222
- return self._recursive_subst(output, current_path=str(jsonpath), always_list=always_list)
261
+ if relative_path_anchor == current_path:
262
+ path_fields = self._extract_fields(jsonpath)
263
+ has_template_key = any(field in self._template_keys for field in path_fields)
264
+ _rel_path_anchor = current_path if has_template_key else str(jsonpath)
265
+ else:
266
+ _rel_path_anchor = relative_path_anchor
267
+ return self._recursive_subst(
268
+ output,
269
+ current_path=str(jsonpath),
270
+ always_list=always_list,
271
+ relative_path_anchor=_rel_path_anchor
272
+ )
273
+
274
+ def get_relative_path(new_path):
275
+ return new_path if current_path == relative_path_anchor else relative_path_anchor
223
276
 
224
277
  if isinstance(templ, str):
225
- match_whole_str = self._pattern_template.fullmatch(templ)
278
+ match_whole_str = self._pattern_template.fullmatch(templ) or self._pattern_template_unpack.fullmatch(templ)
226
279
  if match_whole_str:
227
280
  return get_address_value(match_whole_str)
228
281
  return self._pattern_template.sub(
@@ -232,8 +285,9 @@ class TemplateFiller:
232
285
  if isinstance(templ, list):
233
286
  out = []
234
287
  for idx, elem in enumerate(templ):
288
+ new_path = f"{current_path}[{idx}]"
235
289
  elem_filled = self._recursive_subst(
236
- elem, f"{current_path}[{idx}]", always_list
290
+ templ=elem, current_path=new_path, always_list=always_list, relative_path_anchor=get_relative_path(new_path),
237
291
  )
238
292
  if isinstance(elem, str) and self._pattern_template_unpack.fullmatch(elem):
239
293
  out.extend(elem_filled)
@@ -243,12 +297,15 @@ class TemplateFiller:
243
297
  if isinstance(templ, dict):
244
298
  new_dict = {}
245
299
  for key, val in templ.items():
246
- key_filled = self._recursive_subst(key, current_path, always_list=False)
247
- if self._ignore_key_regex and _re.match(self._ignore_key_regex, key_filled):
300
+ key_filled = self._recursive_subst(
301
+ templ=key, current_path=current_path, always_list=False, relative_path_anchor=relative_path_anchor,
302
+ )
303
+ if key_filled in self._template_keys:
248
304
  new_dict[key_filled] = val
249
305
  continue
306
+ new_path = f"{current_path}.'{key_filled}'"
250
307
  new_dict[key_filled] = self._recursive_subst(
251
- val, f"{current_path}.'{key_filled}'", always_list=always_list
308
+ templ=val, current_path=new_path, always_list=always_list, relative_path_anchor=get_relative_path(new_path),
252
309
  )
253
310
  return new_dict
254
311
  return templ
@@ -264,3 +321,17 @@ class TemplateFiller:
264
321
  num_periods = 0
265
322
  rest_of_string = s
266
323
  return rest_of_string, num_periods
324
+
325
+ @staticmethod
326
+ def _extract_fields(jsonpath):
327
+ def _recursive_extract(expr):
328
+ if hasattr(expr, "fields"):
329
+ fields.extend(expr.fields)
330
+ if hasattr(expr, "right"):
331
+ _recursive_extract(expr.right)
332
+ if hasattr(expr, "left"):
333
+ _recursive_extract(expr.left)
334
+ return
335
+ fields = []
336
+ _recursive_extract(jsonpath)
337
+ return fields