fleche.caches ============= .. py:module:: fleche.caches Attributes ---------- .. autoapisummary:: fleche.caches.logger fleche.caches.DigestedIterable fleche.caches.DigestedDict Exceptions ---------- .. autoapisummary:: fleche.caches.Rejected Classes ------- .. autoapisummary:: fleche.caches.BaseCache fleche.caches.Cache fleche.caches.ReadOnlyCache fleche.caches.FilteredCache fleche.caches.RefreshingCache fleche.caches.CacheStack fleche.caches.SizeLimitedMixin fleche.caches.SizeLimitedCache Module Contents --------------- .. py:data:: logger .. py:exception:: Rejected Bases: :py:obj:`Exception` Cache refused to cache the call for some reason or other. .. py:data:: DigestedIterable .. py:data:: DigestedDict .. py:class:: BaseCache Bases: :py:obj:`abc.ABC` Helper class that provides a standard way to create an ABC using inheritance. .. py:method:: save(call: fleche.call.Call) -> str :abstractmethod: .. py:method:: load(key: str, lazy: bool = False) -> fleche.call.Call load(key: str, lazy: bool = True) -> fleche.call.LazyCall .. py:method:: load_value(key: str) -> Any :abstractmethod: .. py:method:: evict(key: str | fleche.digest.Digest) -> None :abstractmethod: .. py:method:: contains(key: str) -> bool .. py:method:: transfer(other: BaseCache, pop: bool = False, overwrite: bool = False) -> None Transfer all calls from this cache to another cache. :param other: The destination cache. :param pop: If True, evict transferred keys from the source cache after moving. :param overwrite: If True, overwrite existing entries in the target cache. If False (default), skip entries that already exist in the target. .. py:method:: readonly() -> ReadOnlyCache Return a read-only view of this cache. .. py:method:: push(cache: BaseCache) -> CacheStack .. py:method:: expand(key: fleche.digest.Digest | str) -> fleche.digest.Digest :abstractmethod: Expand a short digest prefix to its full-length digest. :param key: the short digest prefix to expand :type key: str or :class:`Digest` :returns: the full-length digest :rtype: :class:`Digest` :raises KeyError: if the key is not found :raises AmbiguousDigestError: if the prefix matches more than one entry .. py:method:: shrink(key: fleche.digest.Digest | str) -> fleche.digest.Digest :abstractmethod: Find the shortest substring that is still an unambigious reference to the same call. .. warning:: This is a property of how many values there are in your storage! A key returned from this function may become ambigious in the future when more values are added. Do not rely on this function in your programs, it is provided as a convenience for users only! :param key: the key to shorten :type key: str or :class:`Digest` :returns: shortest key possible :rtype: :class:`Digest` :raises AmbiguousDigestError: if no shorter key is possible .. py:method:: _query(call: BaseCache._query.call) -> Iterable[fleche.call.LazyCall] :abstractmethod: .. py:method:: query(call: BaseCache.query.call) -> fleche.query.QueryIterator .. py:method:: table() -> pandas.DataFrame Return a pandas DataFrame summarizing cached calls via query(). This implementation uses a fully-wildcard Call template to retrieve all calls through ``self.query`` and then flattens metadata keys into top-level columns for convenience. Arguments and results are elided. The DataFrame index will be the lookup key (digest) of each call. :returns: table of all calls on cache :rtype: :class:`pandas.DataFrame` .. py:method:: filter(predicate: Callable[[fleche.call.Call | fleche.call.LazyCall], bool] | fleche.call.Call) -> FilteredCache Create a read-only view of this cache that only exposes calls matching the predicate. :param predicate: A function that takes a Call or LazyCall and returns True if it should be included in the new cache, or a Call object to use as a template. :returns: A read-only view of the cache. :rtype: FilteredCache .. py:class:: Cache Bases: :py:obj:`BaseCache` Helper class that provides a standard way to create an ABC using inheritance. .. py:attribute:: values :type: fleche.storage.Storage .. py:attribute:: calls :type: fleche.storage.CallStorage .. py:attribute:: _calls :type: dataclasses.InitVar[fleche.storage.CallStorage | fleche.storage.Storage] .. py:method:: __post_init__(_calls) .. py:method:: load_value(key) .. py:method:: _handle_args_save(value) .. py:method:: _handle_args_load(key) .. py:method:: save(call: fleche.call.Call) -> str .. py:method:: _decode_call(call: fleche.call.Call, lazy: bool = False) -> fleche.call.Call _decode_call(call: fleche.call.Call, lazy: bool = True) -> fleche.call.LazyCall .. py:method:: load(key: str, lazy: bool = False) -> fleche.call.Call load(key: str, lazy: bool = True) -> fleche.call.LazyCall .. py:method:: contains(key: str) -> bool .. py:method:: expand(key: fleche.digest.Digest | str) -> fleche.digest.Digest Expand a short digest prefix to its full-length digest. :param key: the short digest prefix to expand :type key: str or :class:`Digest` :returns: the full-length digest :rtype: :class:`Digest` :raises KeyError: if the key is not found :raises AmbiguousDigestError: if the prefix matches more than one entry .. py:method:: shrink(key: fleche.digest.Digest | str) -> fleche.digest.Digest Find the shortest substring that is still an unambigious reference to the same call. .. warning:: This is a property of how many values there are in your storage! A key returned from this function may become ambigious in the future when more values are added. Do not rely on this function in your programs, it is provided as a convenience for users only! :param key: the key to shorten :type key: str or :class:`Digest` :returns: shortest key possible :rtype: :class:`Digest` :raises AmbiguousDigestError: if no shorter key is possible .. py:method:: _query(call: Cache._query.call) -> Iterable[fleche.call.LazyCall] Query for cached calls that match a template and return decoded results. This delegates to the underlying :meth:`CallStorage.query` using the provided template ``call``. Any digested argument values and the result are decoded via this cache's value storage before yielding. :param call: A ``Call`` instance used as a template; fields set to ``None`` act as wildcards. For arguments and result, comparisons follow digest semantics (i.e., values are matched by their digest). :Yields: *Call | LazyCall* -- Matching calls with arguments and result decoded from digests where possible. .. py:method:: evict(key: str | fleche.digest.Digest) -> None .. py:method:: redigest() -> None Ensures consistent cache keys in case digest function changed. This may take time depending on cache size. .. py:class:: ReadOnlyCache Bases: :py:obj:`BaseCache` A cache that can only be read from. .. py:attribute:: cache :type: BaseCache .. py:method:: save(call: fleche.call.Call) .. py:method:: load(key, lazy: bool = True) .. py:method:: expand(key: fleche.digest.Digest | str) -> fleche.digest.Digest Expand a short digest prefix to its full-length digest. :param key: the short digest prefix to expand :type key: str or :class:`Digest` :returns: the full-length digest :rtype: :class:`Digest` :raises KeyError: if the key is not found :raises AmbiguousDigestError: if the prefix matches more than one entry .. py:method:: shrink(key: fleche.digest.Digest | str) -> fleche.digest.Digest Find the shortest substring that is still an unambigious reference to the same call. .. warning:: This is a property of how many values there are in your storage! A key returned from this function may become ambigious in the future when more values are added. Do not rely on this function in your programs, it is provided as a convenience for users only! :param key: the key to shorten :type key: str or :class:`Digest` :returns: shortest key possible :rtype: :class:`Digest` :raises AmbiguousDigestError: if no shorter key is possible .. py:method:: evict(key: str | fleche.digest.Digest) -> None .. py:method:: load_value(key) .. py:method:: contains(key: str) -> bool .. py:method:: _query(call: ReadOnlyCache._query.call) -> Iterable[fleche.call.LazyCall] Forward queries to the wrapped cache. :param call: A template ``Call`` where ``None`` fields act as wildcards. :Yields: *Call | LazyCall* -- Results yielded by the wrapped cache's ``query`` method. .. py:class:: FilteredCache Bases: :py:obj:`ReadOnlyCache` A read-only view of a cache that only exposes calls matching a predicate. .. py:attribute:: predicate :type: Callable[[fleche.call.Call | fleche.call.LazyCall], bool] .. py:method:: load(key, lazy: bool = True) .. py:method:: _query(call: FilteredCache._query.call) -> Iterable[fleche.call.LazyCall] Forward queries to the wrapped cache. :param call: A template ``Call`` where ``None`` fields act as wildcards. :Yields: *Call | LazyCall* -- Results yielded by the wrapped cache's ``query`` method. .. py:class:: RefreshingCache Bases: :py:obj:`BaseCache` A cache that forces re-execution by always missing on load. It forwards saves and value loads to an underlying cache, allowing new results to be stored while ensuring that existing ones are ignored for the duration of its use. This is necessary to handle nested fleche calls during a rerun, otherwise forcing them to re-execute would be awkward. .. py:attribute:: cache :type: BaseCache .. py:method:: save(call: fleche.call.Call) -> str .. py:method:: load(key: str, lazy: bool = False) -> fleche.call.Call load(key: str, lazy: bool = True) -> fleche.call.LazyCall .. py:method:: load_value(key: str) -> Any .. py:method:: contains(key: str) -> bool .. py:method:: evict(key: str | fleche.digest.Digest) -> None .. py:method:: expand(key: fleche.digest.Digest | str) -> fleche.digest.Digest Expand a short digest prefix to its full-length digest. :param key: the short digest prefix to expand :type key: str or :class:`Digest` :returns: the full-length digest :rtype: :class:`Digest` :raises KeyError: if the key is not found :raises AmbiguousDigestError: if the prefix matches more than one entry .. py:method:: shrink(key: fleche.digest.Digest | str) -> fleche.digest.Digest Find the shortest substring that is still an unambigious reference to the same call. .. warning:: This is a property of how many values there are in your storage! A key returned from this function may become ambigious in the future when more values are added. Do not rely on this function in your programs, it is provided as a convenience for users only! :param key: the key to shorten :type key: str or :class:`Digest` :returns: shortest key possible :rtype: :class:`Digest` :raises AmbiguousDigestError: if no shorter key is possible .. py:method:: _query(call: RefreshingCache._query.call) -> fleche.query.QueryIterator .. py:class:: CacheStack Bases: :py:obj:`BaseCache` Represents a combination of caches. Saving will always hit the lowest level, while loading will traverse up. .. py:attribute:: stack :type: tuple[BaseCache, Ellipsis] .. py:method:: save(call: fleche.call.Call) .. py:method:: load(key, lazy: bool = True) .. py:method:: load_value(key) .. py:method:: contains(key: str) -> bool .. py:method:: push(cache: BaseCache) -> CacheStack .. py:method:: evict(key: str | fleche.digest.Digest) -> None .. py:method:: expand(key: fleche.digest.Digest | str) -> fleche.digest.Digest Expand a short digest prefix to its full-length digest. :param key: the short digest prefix to expand :type key: str or :class:`Digest` :returns: the full-length digest :rtype: :class:`Digest` :raises KeyError: if the key is not found :raises AmbiguousDigestError: if the prefix matches more than one entry .. py:method:: shrink(key: fleche.digest.Digest | str) -> fleche.digest.Digest Find the shortest substring that is still an unambigious reference to the same call. .. warning:: This is a property of how many values there are in your storage! A key returned from this function may become ambigious in the future when more values are added. Do not rely on this function in your programs, it is provided as a convenience for users only! :param key: the key to shorten :type key: str or :class:`Digest` :returns: shortest key possible :rtype: :class:`Digest` :raises AmbiguousDigestError: if no shorter key is possible .. py:method:: _query(call: CacheStack._query.call) -> Iterable[fleche.call.LazyCall] Aggregate query results across the stack, avoiding duplicates. The caches are queried from bottom to top. Results are deduplicated by their lookup key (via ``Call.to_lookup_key()``) and yielded in the order they are first seen. :param call: A template ``Call`` where ``None`` fields act as wildcards. :Yields: *Call | LazyCall* -- Matching calls from any cache in the stack, without duplicates. .. py:class:: SizeLimitedMixin Bases: :py:obj:`BaseCache` Mixin that enforces a maximum number of cached calls with random eviction. Combine this with :class:`Cache` (mixin first in MRO) to get a size-limited cache:: @dataclass class SizeLimitedCache(SizeLimitedMixin, Cache): max_size: int When a new call is saved and the number of cached calls exceeds ``max_size``, a call record is selected for eviction via :meth:`_pick_eviction_target`. Value storage is intentionally left untouched. The concrete class must provide a ``max_size`` integer, which is provided automatically when mixed with :class:`Cache`. .. py:attribute:: max_size :type: int .. py:method:: __post_init__(*args, **kwargs) .. py:method:: _pick_eviction_target(keys: list[str]) -> str Select the call to evict from a sample of cached call keys. The default implementation chooses uniformly at random. Override this method to implement a different eviction policy without touching any other part of the class. :param keys: A non-empty list of all tracked call keys. :returns: The key of the call that should be evicted. .. py:method:: _enforce_size_limit() -> None Evict call records until the cache is within ``max_size``. .. py:method:: save(call: SizeLimitedMixin.save.call) -> str .. py:method:: evict(key: str | fleche.digest.Digest) -> None .. py:class:: SizeLimitedCache Bases: :py:obj:`SizeLimitedMixin`, :py:obj:`Cache` A :class:`Cache` that enforces a maximum number of cached calls. When a new call is saved and the number of cached calls exceeds ``max_size``, a call record is selected for eviction via :meth:`_pick_eviction_target`. The default policy evicts uniformly at random; override :meth:`_pick_eviction_target` to change this. :param values: Value storage (forwarded to :class:`Cache`). :param _calls: Call storage (forwarded to :class:`Cache`). :param max_size: Maximum number of calls to keep. .. py:attribute:: max_size :type: int