Skip to content

Commit 87453ed

Browse files
committed
Release version 0.0.0.dev62
1 parent 2c514d1 commit 87453ed

File tree

5 files changed

+41
-14
lines changed

5 files changed

+41
-14
lines changed

pkg/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ namespaces = true
1717
# ----------------------------------------- Project Metadata -------------------------------------
1818
#
1919
[project]
20-
version = "0.0.0.dev61"
20+
version = "0.0.0.dev62"
2121
name = "PySerials"
2222
dependencies = [
2323
"jsonschema >= 4.21.0, < 5",
@@ -26,8 +26,8 @@ dependencies = [
2626
"ruamel.yaml >= 0.18", # https://yaml.readthedocs.io/en/stable/
2727
"ruamel.yaml.string >= 0.1.1, < 1",
2828
"tomlkit >= 0.11.8, < 0.14", # https://tomlkit.readthedocs.io/en/stable/,
29-
"MDit == 0.0.0.dev58",
30-
"ExceptionMan == 0.0.0.dev58",
29+
"MDit == 0.0.0.dev59",
30+
"ExceptionMan == 0.0.0.dev59",
3131
"ProtocolMan == 0.0.0.dev2",
3232
]
3333
requires-python = ">=3.10"

pkg/requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ jsonpath-ng >= 1.6.1, < 2
44
ruamel.yaml >= 0.18
55
ruamel.yaml.string >= 0.1.1, < 1
66
tomlkit >= 0.11.8, < 0.14
7-
MDit == 0.0.0.dev58
8-
ExceptionMan == 0.0.0.dev58
7+
MDit == 0.0.0.dev59
8+
ExceptionMan == 0.0.0.dev59
99
ProtocolMan == 0.0.0.dev2

pkg/src/pyserials/exception/update.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ def __init__(
4949
return
5050

5151

52-
class PySerialsUpdateDictFromAddonError(PySerialsUpdateException):
53-
"""Base class for all exceptions raised by `pyserials.update.dict_from_addon`.
52+
class PySerialsUpdateRecursiveDataError(PySerialsUpdateException):
53+
"""Base class for all exceptions raised by `pyserials.update.recursive_update`.
5454
5555
Attributes
5656
----------

pkg/src/pyserials/nested_dict.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ def __init__(
3636
relative_key_key: str | None = None,
3737
implicit_root: bool = True,
3838
getter_function_name: str = "get",
39+
skip_key_func: Callable[[list[str]], bool] | None = None,
3940
):
4041
self._data = data or {}
4142
self._templater = _ps.update.TemplateFiller(
@@ -62,6 +63,7 @@ def __init__(
6263
relative_key_key=relative_key_key,
6364
implicit_root=implicit_root,
6465
getter_function_name=getter_function_name,
66+
skip_key_func=skip_key_func,
6567
)
6668
return
6769

pkg/src/pyserials/update.py

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def recursive_update(
2929
types: dict[type | tuple[type, ...], UPDATE_OPTIONS | tuple[UPDATE_OPTIONS, UPDATE_OPTIONS]] | None = None,
3030
paths: dict[str, UPDATE_OPTIONS] | None = None,
3131
constructor: Callable[[], Any] | None = None,
32+
undefined_new: UPDATE_OPTIONS = "write",
33+
undefined_existing: UPDATE_OPTIONS = "skip",
3234
type_mismatch: UPDATE_OPTIONS = "raise",
3335
) -> dict[str, list[str]]:
3436
"""Recursively update a complex data structure using another data structure.
@@ -108,9 +110,8 @@ def recursive_update(
108110
types = {list: ("write", lambda data: (data[0] + data[1], "append"))}
109111
```
110112
111-
The default behavior for any data type not specified in this argument is
112-
`("write", "skip")`, meaning that the key/attribute will be written in the source data
113-
if it does not exist, and ignored if it does.
113+
The default behavior for any data type not specified in this argument
114+
is determined by the `undefined_new` and `undefined_existing` arguments.
114115
paths
115116
Update behavior for specific keys using JSONPath expressions.
116117
This is the same as the `types` argument, but targeting specific keys
@@ -122,6 +123,14 @@ def recursive_update(
122123
This is used when the addon data contains a recursive key/attribute
123124
that is not present in the source data. If not provided,
124125
the type of the addon value will be used to create a new instance.
126+
undefined_new
127+
Behavior for when a non-recursive data with no defined behavior
128+
in the `types` argument is found in the addon data
129+
but not in the source data.
130+
undefined_existing
131+
Behavior for when a non-recursive data with no defined behavior
132+
in the `types` argument is found in the addon data
133+
and in the source data.
125134
type_mismatch
126135
Behavior for when a key/attribute in the source data
127136
is not a recursive type, but the corresponding key/attribute
@@ -176,8 +185,12 @@ def raise_error(typ: Literal["duplicate", "type_mismatch"]):
176185
fn_add_items, _, _, _, fn_add_construct = add_funcs
177186

178187
for key, value in fn_add_items(add):
179-
fullpath = f"{path}.{key}"
180-
full_jpath = _jsonpath.parse(f"{path}.'{key}'") # quote to avoid JSONPath syntax errors
188+
fullpath = f"{path}.'{key}'"
189+
try:
190+
full_jpath = _jsonpath.parse(fullpath) # quote to avoid JSONPath syntax errors
191+
except Exception as e:
192+
print(fullpath)
193+
raise e
181194
key_exists_in_src = fn_src_contains(src, key)
182195
source_value = fn_src_get(src, key) if key_exists_in_src else None
183196
for jpath_str, matches in jsonpath_match.items():
@@ -221,7 +234,7 @@ def raise_error(typ: Literal["duplicate", "type_mismatch"]):
221234
else:
222235
# addon value is of a non-recursive type that does not have any defined behavior;
223236
# Apply the default behavior for of ("write", "skip") for the key.
224-
apply("skip" if key_exists_in_src else "write")
237+
apply(undefined_existing if key_exists_in_src else undefined_new)
225238
return
226239

227240
type_to_arg = {list: ("write", lambda data: (data[0] + data[1], "append"))} | (types or {})
@@ -307,6 +320,7 @@ def __init__(
307320
relative_key_key: str | None = None,
308321
implicit_root: bool = True,
309322
getter_function_name: str = "get",
323+
skip_key_func: Callable[[list[str]], bool] | None = None,
310324
):
311325
self._marker_start_value = marker_start_value
312326
self._marker_end_value = marker_end_value
@@ -329,6 +343,7 @@ def __init__(
329343
self._template_keys = relative_template_keys or []
330344
self._relative_key_key = relative_key_key
331345
self._getter_function_name = getter_function_name
346+
self._skip_func = skip_key_func
332347

333348
self._pattern_value: dict[int, _RegexPattern] = {}
334349
self._data = None
@@ -368,7 +383,11 @@ def getter_function(path: str, default: Any = None, search: bool = False):
368383
code_lines = ["def __inline_code__():"]
369384
code_lines.extend([f" {line}" for line in code_str.strip("\n").splitlines()])
370385
code_str_full = "\n".join(code_lines)
371-
global_context = self._code_context.copy() | {self._getter_function_name: getter_function}
386+
global_context = self._code_context.copy() | {
387+
self._getter_function_name: getter_function,
388+
"__current_path__": current_path,
389+
"__relative_path_anchor__": relative_path_anchor
390+
}
372391
for name, partial_func_data in self._code_context_partial.items():
373392
if isinstance(partial_func_data, tuple):
374393
func, arg_name = partial_func_data
@@ -428,6 +447,7 @@ def get_address_value(match: _re.Match | str, return_all_matches: bool = False,
428447
return output, True
429448
return output
430449
path_expr = self._concat_json_paths(root_path_expr, path_expr)
450+
# print("IN GET ADD VAL", path_expr)
431451
cached_result = self._visited_paths.get(path_expr)
432452
if cached_result:
433453
value, matched = cached_result
@@ -535,6 +555,11 @@ def raise_error(path_invalid: str, description_template: str, exception: Excepti
535555
template_end=self._marker_end_value,
536556
) from exception
537557

558+
# print("IN MAIN", self._extract_fields(current_path))
559+
560+
if self._skip_func and self._skip_func(self._extract_fields(current_path)):
561+
return templ
562+
538563
if current_path in self._visited_paths:
539564
return self._visited_paths[current_path][0]
540565

0 commit comments

Comments
 (0)