Source code for otio_sync_core.proxy
"""Transparent proxy that intercepts attribute writes and forwards them to OTIOPatcher."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any
import opentimelineio as otio
if TYPE_CHECKING:
from .patcher import OTIOPatcher
[docs]
class OTIOSyncProxy:
"""Transparent proxy around an OTIO :class:`~opentimelineio.core.SerializableObject`.
Attribute reads are passed through to the wrapped object unchanged. Attribute
writes are applied to the wrapped object **and** forwarded to the
:class:`~otio_sync_core.patcher.OTIOPatcher` as a ``set_property`` change,
so that remote peers stay in sync without the caller needing to be aware of the
sync layer.
Child OTIO objects returned by ``__getattr__`` are themselves wrapped in a new
:class:`OTIOSyncProxy` so that nested attribute writes are also captured.
:param obj: The OTIO object to wrap.
:param patcher: The :class:`~otio_sync_core.patcher.OTIOPatcher` (or compatible)
that owns this object.
:param parent_path: Dot-separated path prefix used when constructing the property
path for nested objects.
"""
def __init__(
self,
obj: otio.core.SerializableObject,
patcher: OTIOPatcher,
parent_path: str = "",
) -> None:
object.__setattr__(self, "_obj", obj)
object.__setattr__(self, "_patcher", patcher)
object.__setattr__(self, "_path", parent_path)
@property
def __class__(self) -> type: # type: ignore[override]
return self._obj.__class__
def __getattr__(self, name: str) -> Any:
"""Return the attribute from the wrapped object.
If the value is itself a :class:`~opentimelineio.core.SerializableObject`
it is wrapped in a new :class:`OTIOSyncProxy` so that nested writes are
also intercepted.
:param name: Attribute name.
:returns: Attribute value, proxied if it is an OTIO object.
"""
val = getattr(self._obj, name)
if isinstance(val, otio.core.SerializableObject):
return OTIOSyncProxy(val, self._patcher, "")
return val
def __setattr__(self, name: str, value: Any) -> None:
"""Write *value* to the wrapped object and broadcast the change.
Private proxy attributes (``_obj``, ``_patcher``, ``_path``) are stored
directly on the proxy instance and are never forwarded.
:param name: Attribute name.
:param value: New value.
"""
if name in ("_obj", "_patcher", "_path"):
object.__setattr__(self, name, value)
return
setattr(self._obj, name, value)
if getattr(self._patcher, "_is_syncing", False):
return
guid: str | None = None
if isinstance(self._obj, otio.core.SerializableObject):
if "sync" in self._obj.metadata and "guid" in self._obj.metadata["sync"]:
guid = self._obj.metadata["sync"]["guid"]
if guid:
path = name if not self._path else f"{self._path}/{name}"
self._patcher.set_property(guid, path, value)
def __repr__(self) -> str:
return repr(self._obj)
def __str__(self) -> str:
return str(self._obj)