class _Update:
- def __init__(self, path: tuple[str, ...], value: Any) -> None:
+ def __init__(self, path: tuple[str, ...], value: Any, force_log=False
+ ) -> None:
self.path = path
self.value = value
+ self.force_log = force_log
for cls in [cls for cls in locals().values()
if isinstance(cls, type)
and issubclass(cls, _UpdatingNode)
def decremented(self) -> Self:
'Create copy with .path reduced by leftmost step.'
- return self.__class__(self.path[1:], self.value)
+ return self.__class__(self.path[1:], self.value, self.force_log)
class _UpdatingNode(AutoAttrMixin):
def recursive_set_and_report_change(
self, update: _Update) -> Optional[tuple[LogScope, Any]]:
'Apply update, return if that actually made a difference.'
+ update.force_log = update.force_log or (not self._is_set(update.key))
node = self._get(update.key)
if len(update.path) > 1:
return node.recursive_set_and_report_change(update.decremented())
self, old_value: '_UpdatingNode', update: _Update
) -> Optional[tuple[LogScope, Any]]:
scope = self._scope(update.key)
+ do_report = update.force_log
if update.value is None:
if self._is_set(update.key):
self._unset(update.key)
- return scope, None
- return None
- if old_value == update.value:
- return None
- self._set(update.key, update.value)
- return scope, update.value
+ do_report |= True
+ elif old_value != update.value:
+ self._set(update.key, update.value)
+ do_report |= True
+ return (scope, update.value) if do_report else None
def _get(self, key: str) -> Any:
return getattr(self, key)