Source code for tyche.references

"""
This module contains functionality to represent references to concepts
or roles. These references allow the value of concepts or roles to be
fetched from individuals, and updated with new values within individuals.
"""
from typing import Generic, TypeVar, Callable, Optional


[docs]class TycheReferencesException(Exception): """ An exception type that is thrown when errors occur in the construction or use of references. """ def __init__(self, message: str): self.message = "TycheReferencesException: " + message
SymbolType = TypeVar("SymbolType")
[docs]class SymbolReference(Generic[SymbolType]): """ Represents a reference to the value of a symbol. """ symbol: str def __init__(self, symbol: str): self.symbol = symbol def is_mutable(self) -> bool: raise NotImplementedError(f"is_mutable is not implemented for {type(self).__name__}") def get(self, obj: object) -> SymbolType: raise NotImplementedError(f"get is not implemented for {type(self).__name__}") def set(self, obj: object, value: SymbolType): raise NotImplementedError(f"set is not implemented for {type(self).__name__}") def bake(self, obj: object) -> 'BakedSymbolReference': return BakedSymbolReference(self, obj)
[docs]class BakedSymbolReference(Generic[SymbolType]): """ Represents a reference to the value of a symbol, with the object that the value is accessed from baked in. """ def __init__(self, ref: SymbolReference[SymbolType], obj: object): self.ref = ref self.obj = obj def is_mutable(self) -> bool: return self.ref.is_mutable() def get(self) -> SymbolType: return self.ref.get(self.obj) def set(self, value: SymbolType): self.ref.set(self.obj, value)
[docs]class FieldSymbolReference(Generic[SymbolType], SymbolReference[SymbolType]): """ Represents a reference to a field in an object. """ def __init__(self, symbol: str, *, field_name: Optional[str] = None): super().__init__(symbol) self.field_name = symbol if field_name is None else field_name def is_mutable(self) -> bool: return True def get(self, obj: object) -> SymbolType: return getattr(obj, self.field_name) def set(self, obj: object, value: SymbolType): if not hasattr(obj, self.field_name): raise TycheReferencesException(f"The object does not contain the field {self.symbol}. The object is {obj}") setattr(obj, self.field_name, value)
[docs]class FunctionSymbolReference(Generic[SymbolType], SymbolReference[SymbolType]): """ Represents a reference to a mutable property in an object. """ def __init__( self, symbol: str, fget: Callable[[object], SymbolType], fset: Optional[Callable[[object, SymbolType], None]] = None ): super().__init__(symbol) self.symbol = symbol self.fget = fget self.fset = fset def is_mutable(self) -> bool: return self.fset is not None def get(self, obj: object) -> SymbolType: return self.fget(obj) def set(self, obj: object, value: SymbolType): if self.fset is None: raise TycheReferencesException( f"This function reference to {self.symbol} is not mutable") self.fset(obj, value)
GuardedSymbolType = TypeVar("GuardedSymbolType")
[docs]class GuardedSymbolReference( Generic[SymbolType, GuardedSymbolType], SymbolReference[SymbolType] ): """ Represents a mutable reference where the accessed value is different to the value stored in the reference. The referenced value is transformed when accessed or modified. """ def __init__( self, ref: SymbolReference[GuardedSymbolType], get_transform: Callable[[GuardedSymbolType], SymbolType], set_transform: Optional[Callable[[SymbolType], GuardedSymbolType]] = None ): super().__init__(ref.symbol) self.ref = ref self.get_transform = get_transform self.set_transform = set_transform def is_mutable(self) -> bool: return self.set_transform is not None and self.ref.is_mutable() def get(self, obj: object) -> SymbolType: return self.get_transform(self.ref.get(obj)) def set(self, obj: object, value: SymbolType): if self.set_transform is None: raise TycheReferencesException( f"This guarded reference to {self.symbol} is not mutable") self.ref.set(obj, self.set_transform(value))