Source code for mincepy.refs
# -*- coding: utf-8 -*-
"""References module"""
from typing import Optional
from . import exceptions
from . import records
from . import types
from . import type_ids
__all__ = ("ObjRef", "ref")
[docs]class ObjRef(types.SavableObject):
"""A reference to an object instance"""
TYPE_ID = type_ids.OBJ_REF_TYPE_ID
IMMUTABLE = True
_obj = None
_sid: Optional[records.SnapshotId] = None
_loader = None
def __init__(self, obj=None):
super().__init__()
assert not (
obj is not None and types.is_primitive(obj)
), "Can't create a reference to a primitive type"
self._obj = obj
def __bool__(self) -> bool:
"""Test if this is a null reference"""
return self._obj is not None or self._sid is not None
def __str__(self) -> str:
desc = ["ObjRef('"]
if self._obj is not None:
desc.append(str(self._obj))
else:
desc.append(str(self._sid))
desc.append("')")
return "".join(desc)
def __repr__(self) -> str:
return f"ObjRef({self._obj if self._obj is not None else self._sid})"
def __call__(self, update=False):
"""Get the object being referenced. If update is called then the latest version
will be loaded from the historian"""
if self._obj is None:
# This means we were loaded and need to load the object
if self._sid is None:
raise RuntimeError("Cannot dereference a None reference")
# Cache the object
self._obj = self._loader.load(self._sid)
assert ( # nosec: intentional internal assert
self._obj is not None
), f"Loader did not load object using SID {self._sid}"
self._sid = None
self._loader = None
elif update:
try:
self._historian.sync(self._obj)
except exceptions.NotFound:
pass # Object must never have been saved and is therefore up to date
return self._obj
def __eq__(self, other) -> bool:
if not isinstance(other, ObjRef):
return False
if self._obj is not None:
return id(self._obj) == id(other._obj)
return self._sid == other._sid
[docs] def yield_hashables(self, hasher):
if self._obj is not None:
yield from hasher.yield_hashables(id(self._obj))
else:
# This will also work if ref is None
yield from hasher.yield_hashables(self._sid)
[docs] def save_instance_state(self, saver):
if self._obj is not None:
sid = saver.get_snapshot_id(self._obj)
else:
sid = self._sid
if sid is not None:
return sid.to_dict()
return None
[docs] def load_instance_state(self, saved_state, loader):
super().load_instance_state(saved_state, loader)
# Rely on class default values for members
if saved_state is not None:
if isinstance(saved_state, list):
# Legacy version
self._sid = records.SnapshotId(*saved_state)
else:
# New version is dict
self._sid = records.SnapshotId(**saved_state)
self._loader = loader
[docs]def ref(obj=None) -> ObjRef:
"""Create an object reference"""
return ObjRef(obj)
HISTORIAN_TYPES = (ObjRef,)