Source code for sqlatypemodel.mixin.serialization

"""Pickle state management and serialization helpers."""

from __future__ import annotations

from typing import Any

from sqlatypemodel.mixin._introspection_data import _LIB_ATTRS
from sqlatypemodel.util import constants


[docs] def cleanup_pickle_state(state: Any) -> Any: """Remove unpicklable attributes (like weakrefs) from the state dict. Args: state: The state object (usually a dict) to be cleaned. Returns: The cleaned state object safe for pickling. """ if not isinstance(state, dict): return state # Optimize: Fast bulk removal if possible, otherwise individual pop # pop(k, None) is safe and fast for key in _LIB_ATTRS: state.pop(key, None) # Handle nested __dict__ which some pickle implementations use instance_dict = state.get("__dict__") if instance_dict and isinstance(instance_dict, dict): for key in _LIB_ATTRS: instance_dict.pop(key, None) return state
[docs] def manual_setstate(instance: Any, state: dict[str, Any]) -> None: """Manually restore state when parent class lacks __setstate__. Args: instance: The object instance to restore state into. state: The dictionary containing the state attributes. """ # Optimize: Filter keys first to avoid repeated checks inside loop if # state is large. However, for typical object sizes, simple iteration is # faster than set operations for key, value in state.items(): if key in _LIB_ATTRS: continue try: object.__setattr__(instance, key, value) except (AttributeError, TypeError): # Attribute might be read-only or descriptor that fails on set pass
[docs] def reset_trackable_state(instance: Any) -> None: """Reset library-specific tracking attributes to default values. This is typically called after unpickling to revive the object's change tracking capabilities. Args: instance: The object instance to reset. """ # Optimize: Use EAFP (Easier to Ask for Forgiveness than Permission) # This avoids double lookup (hasattr + delattr) try: delattr(instance, "_parents_store") except AttributeError: pass try: delattr(instance, "_state_inst") except AttributeError: pass object.__setattr__(instance, "_change_suppress_level", 0) object.__setattr__(instance, "_pending_change", False) # Use getattr with default to avoid exception handling logic for # max_nesting_depth if getattr(instance, "_max_nesting_depth", None) is None: default_depth = getattr( instance.__class__, "_max_nesting_depth", constants.DEFAULT_MAX_NESTING_DEPTH, ) object.__setattr__(instance, "_max_nesting_depth", default_depth)