Two defects in the selective overwrite feature (OSW.OverwriteClassParam, OSW._apply_overwrite_policy). Both were found during the assessment in #163 and listed there under "Found but not fixed - needs a design decision". Both are still present on main (v2.5.1, 9351f6b).
1. A property cannot be cleared by assigning an empty value
remove_empty=True is the default. It strips "", [] and {} from the local content before the merge:
|
local_content["jsondata"] = json.loads(param.entity.json(exclude_none=True)) |
|
if param.remove_empty: |
|
remove_empty(local_content["jsondata"]) |
The merge then starts from the remote content and pulls in only those keys that are still present in the stripped local content:
|
new_content["jsondata"] = remote_content["jsondata"] |
The stored value therefore always survives. A caller who assigns "" to clear a property gets no change and no warning. The only workaround is remove_empty=False for the whole call, which also writes every unrelated empty default.
Expected: an empty value that the caller assigned on purpose clears the stored value, subject to the overwrite setting as usual. An empty value that is merely a field default is still stripped.
2. Pages kept by keep existing are indistinguishable from pages that were written
StoreEntityResult declares change_id, pages and failed:
|
class StoreEntityResult(OswBaseModel): |
|
"""Result of store_entity()""" |
|
|
|
change_id: str |
|
"""The ID of the change""" |
|
pages: Dict[str, WtPage] |
|
"""The pages that have been successfully stored, keyed by full page title. |
|
On partial failure this contains only the successfully-stored pages.""" |
|
failed: Dict[str, Exception] = {} |
|
"""Entities that could not be stored, mapped to the exception that caused the |
|
failure. Empty on full success. The key is the full page title where one could |
|
be determined. For an entity whose title or namespace could not be resolved it |
|
falls back to the entity name, then to its uuid, then to 'unknown', so do not |
|
parse this key as 'namespace:title'.""" |
A page that exists and is left untouched under keep existing passes the guard at
|
kept_existing = ( |
|
page.exists |
|
# mirrors the branch order of _apply_overwrite_policy(), which |
|
# tests 'offline is True' before it tests 'keep existing' |
|
and param.offline is not True |
|
and overwrite_class_param.overwrite |
|
== AddOverwriteClassOptions.keep_existing |
|
) |
and still enters pages at
|
created_pages[page.title] = page |
The caller cannot tell which entities were written and which were kept. The docstring of pages reads "The pages that have been successfully stored", which does not hold for a kept page.
Expected: kept pages are identifiable, for example through an additional skipped list. Listing them in pages as well is acceptable, because callers that only need the final set of pages on the wiki depend on it.
Status
A fix for both is in #168, which is open and currently conflicting with main.
Two defects in the selective overwrite feature (
OSW.OverwriteClassParam,OSW._apply_overwrite_policy). Both were found during the assessment in #163 and listed there under "Found but not fixed - needs a design decision". Both are still present onmain(v2.5.1,9351f6b).1. A property cannot be cleared by assigning an empty value
remove_empty=Trueis the default. It strips"",[]and{}from the local content before the merge:osw-python/src/osw/core.py
Lines 1567 to 1569 in 9351f6b
The merge then starts from the remote content and pulls in only those keys that are still present in the stripped local content:
osw-python/src/osw/core.py
Line 1577 in 9351f6b
The stored value therefore always survives. A caller who assigns
""to clear a property gets no change and no warning. The only workaround isremove_empty=Falsefor the whole call, which also writes every unrelated empty default.Expected: an empty value that the caller assigned on purpose clears the stored value, subject to the overwrite setting as usual. An empty value that is merely a field default is still stripped.
2. Pages kept by
keep existingare indistinguishable from pages that were writtenStoreEntityResultdeclareschange_id,pagesandfailed:osw-python/src/osw/core.py
Lines 1707 to 1720 in 9351f6b
A page that exists and is left untouched under
keep existingpasses the guard atosw-python/src/osw/core.py
Lines 1913 to 1920 in 9351f6b
and still enters
pagesatosw-python/src/osw/core.py
Line 1973 in 9351f6b
The caller cannot tell which entities were written and which were kept. The docstring of
pagesreads "The pages that have been successfully stored", which does not hold for a kept page.Expected: kept pages are identifiable, for example through an additional
skippedlist. Listing them inpagesas well is acceptable, because callers that only need the final set of pages on the wiki depend on it.Status
A fix for both is in #168, which is open and currently conflicting with
main.