Source code for simstack.core.engine

from contextvars import ContextVar
from typing import Optional, Any, Iterable
from odmantic import AIOEngine

# TDOD engine remove AIOEngineProxy
[docs] class AIOEngineProxy(AIOEngine): """ A proxy engine that inherits all behavior from AIOEngine but overrides `save`. The overridden `save` will: - If the model has a `save` member function, call it with the engine instance. - Else, if any direct part/attribute of the model has a `save` member function, call those with the engine instance. - Otherwise, fall back to the original AIOEngine.save implementation. Notes: - Supports single model instances or iterables of model instances (list/tuple/set). - Avoids recursion by delegating to super().save for the fallback. """
[docs] async def save(self, obj: Any, *args, **kwargs) -> Any: # type: ignore[override] # Handle collections of models if isinstance(obj, (list, tuple, set)): results = [] for item in obj: results.append(await self._save_one(item, *args, **kwargs)) return results # Single model return await self._save_one(obj, *args, **kwargs)
[docs] async def save_unchecked(self, obj: Any, *args, **kwargs) -> Any: """ Save without checking for custom save methods. This is a direct call to the original AIOEngine.save. """ return await super().save(obj, *args, **kwargs)
async def _save_one(self, model: Any, *args, **kwargs) -> Any: # 1) Try model's own `save` if await self._maybe_call_custom_save(model): return None # 2) Try parts' `save` (shallow scan of attributes and common containers) parts_saved = await self._call_parts_saves(model) # 3) If neither the model nor any part handled saving, fallback to AIOEngine if not parts_saved: return await super().save(model, *args, **kwargs) return None async def _maybe_call_custom_save(self, target: Any) -> bool: """ If `target` has a callable `save`, invoke it with this engine. Tries positional engine and keyword engine forms, awaits if coroutine. Returns True if a save was invoked, False otherwise. """ if not hasattr(target, "save"): return False save_attr = getattr(target, "save") if not callable(save_attr): raise AttributeError(f"Model {target} has no callable `save` method") return await target.save(self) async def _call_parts_saves(self, model: Any) -> bool: """ Inspect immediate parts/attributes of the model and call their `save` if present. Handles: - Direct attributes from vars(model) - Items inside lists/tuples/sets - Values of dicts Returns True if at least one part save was called. """ any_saved = False def iter_parts(root: Any) -> Iterable[Any]: # Direct attributes try: for v in vars(root).values(): yield v except TypeError: # Objects without __dict__ pass # For convenience, also scan common container-typed attributes one level deep try: for v in vars(root).values(): if isinstance(v, (list, tuple, set)): for item in v: yield item elif isinstance(v, dict): for item in v.values(): yield item except TypeError: pass seen_ids = set() for part in iter_parts(model): pid = id(part) if pid in seen_ids: continue seen_ids.add(pid) if await self._maybe_call_custom_save(part): any_saved = True return any_saved
# # current_engine_context: ContextVar[Optional[AIOEngineProxy]] = ContextVar( # "current_engine", default=None # ) # # # def get_current_engine_from_context() -> Optional[AIOEngineProxy]: # """Get the current engine from context""" # return current_engine_context.get()