### HermesCache Quickstart Example Source: https://hermescache.readthedocs.io/en/latest/?badge=latest Demonstrates the main HermesCache API: creating a Hermes facade with a Redis backend, decorating functions and methods with caching, using tags and custom keys, invalidating entries, and cleaning the cache. ```python import hermes.backend.redis cache = hermes.Hermes( hermes.backend.redis.Backend, ttl = 600, host = 'localhost', db = 1, ) @cache def foo(a, b): return a * b class Example: @cache(tags = ('math', 'power'), ttl = 1200) def bar(self, a, b): return a ** b @cache(tags = ('math', 'avg'), key = lambda fn, a, b: f'avg:{a}:{b}') def baz(self, a, b): return (a + b) / 2 print(foo(2, 333)) example = Example() print(example.bar(2, 10)) print(example.baz(2, 10)) foo.invalidate(2, 333) example.bar.invalidate(2, 10) example.baz.invalidate(2, 10) cache.clean(['math']) # invalidate entries tagged 'math' cache.clean() # flush cache ``` -------------------------------- ### Install HermesCache with pip Source: https://hermescache.readthedocs.io/en/latest/?badge=latest Install the HermesCache package using pip. This is the standard installation command. ```bash pip install HermesCache ``` -------------------------------- ### Cache invalidation and cleaning example Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Example usage of the Cache class showing how to invalidate specific entries and clean the cache by tag or flush it entirely. ```python foo.invalidate(2, 333) ``` ```python example.bar.invalidate(2, 10) ``` ```python example.baz.invalidate(2, 10) ``` ```python cache.clean(['math']) # invalidate entries tagged 'math' ``` ```python cache.clean() # flush cache ``` -------------------------------- ### AsyncBackend.startWatch Source: https://hermescache.readthedocs.io/en/latest/reference.html Starts the TTL watching task. Must be called when the asyncio IO loop is running. ```APIDOC ## AsyncBackend.startWatch ### Description Start TTL watching task. ### Method startWatch ### Notes It must be called when `asyncio` IO loop is running. ``` -------------------------------- ### Example of dynamically created cached functions Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Shows two functions f created dynamically in the same module with the same name, each decorated with @cache, returning different results. The example prints the result of calling createF1()(1, 2). ```python def createF1(): @cache def f(a, b): return a + b return f def createF2(): @cache def f(a, b): return a * b return f print(createF1()(1, 2)) ``` -------------------------------- ### HermesCache Hermes usage with Redis backend and decorators Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Example usage of the Hermes class with a Redis backend. It demonstrates creating a cache instance, decorating a function and methods with @cache, using tags and TTL, and a custom key function. The example includes print statements showing how to call the cached functions. ```python import hermes.backend.redis cache = hermes.Hermes( hermes.backend.redis.Backend, ttl = 600, host = 'localhost', db = 1 ) @cache def foo(a, b): return a * b class Example: @cache(tags = ('math', 'power'), ttl = 1200) def bar(self, a, b): return a ** b @cache( tags = ('math', 'avg'), key = lambda fn, *args, **kwargs: 'avg:{0}:{1}'.format(*args), ) def baz(self, a, b): return (a + b) / 2.0 print(foo(2, 333)) example = Example() print(example.bar(2, 10)) print(example.baz(2, 10)) ``` -------------------------------- ### Async caching with Redis backend Source: https://hermescache.readthedocs.io/en/latest/asyncio.html Example of using Hermes with the Redis backend to cache an async function that fetches data from PyPI. The cache decorator is applied with a TTL and tags, and the example shows how to call the cached function and invalidate entries. ```python import aiohttp import hermes.backend.redis cache = hermes.Hermes( hermes.backend.redis.Backend, ttl = 600, host = 'localhost', db = 1, ) @cache(ttl = 365 * 24 * 3600, tags = ['pypi']) async def getHash(version): async with aiohttp.ClientSession() as session: async with session.get('https://pypi.org/pypi/hermescache/json') as resp: return (await resp.json())['releases'][version][0]['md5_digest'] print(await getHash('0.9.0')) print(await getHash('0.8.0')) print(await getHash('0.7.2')) await getHash.invalidate('0.8.0') cache.clean(['pypi']) # invalidate entries tagged 'pypi' ``` -------------------------------- ### Basic caching with Hermes and inprocess backend Source: https://hermescache.readthedocs.io/en/latest/design.html Shows how to cache a function using Hermes with the inprocess backend. The cache key is derived from the callable and arguments, and the return value is stored. The example prints the backend dump showing two cache entries with argument hashes. ```python import hermes.backend.inprocess cache = hermes.Hermes(hermes.backend.inprocess.Backend) @cache def foo(a, b): return a * b foo(2, 2) foo(2, 4) print(cache.backend.dump()) # { # 'cache:entry:foo:bNapzUm_P8fjh8lhIWYgkg': 8, # 'cache:entry:foo:JjwE0zQhMRt_5lfwNNPk1Q': 4 # ↑ # argument hash # } ``` -------------------------------- ### Synchronous Hermes caching example Source: https://hermescache.readthedocs.io/en/latest/asyncio.html Shows the basic synchronous usage of Hermes with a Redis backend. The function fn is cached and called synchronously. This is the baseline for understanding the asynchronous problem. ```python import hermes.backend.redis cache = hermes.Hermes(hermes.backend.redis.Backend) @cache def fn(a): return a + 1 def main(): fn(1) ``` -------------------------------- ### Tagging cache entries with Hermes Source: https://hermescache.readthedocs.io/en/latest/design.html Demonstrates how to tag cache entries using the 'tags' parameter. The example shows the backend dump with tag entries and a cache entry that includes a tag hash in its key. This enables batch invalidation by removing the tag entry. ```python import hermes.backend.inprocess cache = hermes.Hermes(hermes.backend.inprocess.Backend) @cache(tags = ('tag1', 'tag2')) def foo(a, b): return a * b foo(2, 2) print(cache.backend.dump()) # { # 'cache:tag:tag1': 'SeL9JQMnT-_4jRN1nxeoeg', # 'cache:tag:tag2': 'n_OXAKlSjiz5zK5iAEe7zA', # 'cache:entry:foo:JjwE0zQhMRt_5lfwNNPk1Q:h_C-2mB3kWNNrA4r7byxNA': 4 # ↑ # tag hash # } ``` -------------------------------- ### Custom Serialiser for alternative serialisation Source: https://hermescache.readthedocs.io/en/latest/customisation.html Replace the default serialiser with a custom one using msgpack. The example shows setting the serialiser on the cache's mangler. ```python cache = hermes.Hermes(hermes.backend.redis.Backend) # use another serialisation algorithm cache.mangler.serialiser = hermes.Serialiser( msgpack.dumps, lambda b: msgpack.loads(b, strict_map_key = False) ) ``` -------------------------------- ### Usage example for Cached descriptor Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Shows how to use the Cached descriptor by decorating a method with the cache instance. The last attribute access triggers the descriptor's __get__ method. ```python cache = hermes.Hermes() class Model: @cache def calc(self): return 42 m = Model() m.calc ``` -------------------------------- ### Descriptor protocol example with Cached decorator Source: https://hermescache.readthedocs.io/en/latest/reference.html Shows how the Cached decorator implements the non-data descriptor protocol. When an instance method is decorated, attribute access triggers __get__ and returns a bound copy. The example creates a Hermes cache, decorates a method, and accesses it on an instance. ```python cache = hermes.Hermes() class Model: @cache def calc(self): return 42 m = Model() m.calc ``` -------------------------------- ### Example of cache key clash for dynamically defined functions Source: https://hermescache.readthedocs.io/en/latest/reference.html Shows two dynamically defined functions with the same name 'f' in the same module, causing cache keys to clash and both calls to return 3. Use a custom 'key' function to avoid this. ```python def createF1(): @cache def f(a, b): return a + b return f def createF2(): @cache def f(a, b): return a * b return f print(createF1()(1, 2)) print(createF2()(1, 2)) ``` -------------------------------- ### Asynchronous Hermes caching example Source: https://hermescache.readthedocs.io/en/latest/asyncio.html Shows the same Hermes API applied to an async coroutine function. The coroutine awaits a cached synchronous function and uses asyncio.sleep. This illustrates the mixed execution model problem. ```python import asyncio import hermes.backend.redis cache = hermes.Hermes(hermes.backend.redis.Backend) @cache def fn(a): return a + 1 @cache async def coro(a): await asyncio.sleep(fn(1)) return a + 2 async def main(): await coro(1) ``` -------------------------------- ### hermes.CachedCoro.__call__ Source: https://hermescache.readthedocs.io/en/latest/reference.html Get the value of the wrapped coroutine function's coroutine. ```APIDOC ## hermes.CachedCoro.__call__ ### Description Get the value of the wrapped coroutine function's coroutine. ### Method __call__ (async) ### Parameters - *args - positional arguments passed to the wrapped coroutine function - **kwargs - keyword arguments passed to the wrapped coroutine function ### Returns The coroutine of the wrapped coroutine function. ``` -------------------------------- ### hermes.Cached.__call__ Source: https://hermescache.readthedocs.io/en/latest/reference.html Get the value of the wrapped callable. This method is called when the decorated function is invoked. ```APIDOC ## hermes.Cached.__call__ ### Description Get the value of the wrapped callable. ### Method __call__ ### Parameters - *args - positional arguments passed to the wrapped callable - **kwargs - keyword arguments passed to the wrapped callable ### Returns The value returned by the wrapped callable. ``` -------------------------------- ### Memory overhead of stale cache entries after invalidation Source: https://hermescache.readthedocs.io/en/latest/design.html Shows the memory overhead of tag entries and stale cache entries. After calling cache.clean(['tag1']), the tag entry is recreated and the old cache entry remains as garbage. The example prints the backend dump before and after cleaning. ```python import hermes.backend.inprocess cache = hermes.Hermes(hermes.backend.inprocess.Backend) @cache(tags = ('tag1', 'tag2')) def foo(a, b): return a * b foo(2, 2) print(cache.backend.dump()) # { # 'cache:tag:tag1': 'Z_t4-2QfjwvYUc_d75WJgw', # 'cache:tag:tag2': 'M_2pg1-dfS_8OM1Dnd4mXw', # 'cache:entry:foo:JjwE0zQhMRt_5lfwNNPk1Q:ZuT3KKj6gttXtU01-JSbwQ': 4 # } cache.clean(['tag1']) foo(2, 2) print(cache.backend.dump()) # { # recreated tag entry # ↓ # 'cache:tag:tag1': 'TAuY_hhl0a22Nm7P_dWQRA', # 'cache:tag:tag2': 'M_2pg1-dfS_8OM1Dnd4mXw', # 'cache:entry:foo:JjwE0zQhMRt_5lfwNNPk1Q:St3ZHOzerXZxN0qkzXcLVw': 4, # 'cache:entry:foo:JjwE0zQhMRt_5lfwNNPk1Q:ZuT3KKj6gttXtU01-JSbwQ': 4 # ↑ # garbage entry # } ``` -------------------------------- ### Custom Compressor for compression settings Source: https://hermescache.readthedocs.io/en/latest/customisation.html Shows how to disable compression by setting compressor to None, and how to use a custom compressor with snappy. The Compressor constructor takes compress, decompress, decompressError, and an optional threshold. ```python cache = hermes.Hermes(hermes.backend.redis.Backend) # disable compression cache.mangler.compressor = None # use another compression algorithm cache.mangler.compressor = hermes.Compressor( snappy.compress, snappy.decompress, snappy.UncompressError, 32 ) ``` -------------------------------- ### AsyncLock.__enter__ Source: https://hermescache.readthedocs.io/en/latest/reference.html No-op context manager implementation used by hermes.Cached for synchronous code. ```APIDOC ## AsyncLock.__enter__ ### Description No-op context manager implementation. ### Method __enter__ ### Notes Used by `hermes.Cached` for synchronous code. ``` -------------------------------- ### Backend class with __init__ and lock method Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_0a3d1c31add3250f_redis_py.html The Backend class extends AbstractBackend and implements a Redis backend. Its __init__ method accepts a mangler, host, password, port, db, lockconf, and additional kwargs, creating a redis.StrictRedis client and storing the lock configuration. The lock method returns a Lock instance using the mangled key name and the client. ```python class Backend(AbstractBackend): '''Redis backend implementation.''' client: redis.StrictRedis '''Redis client.''' _lockconf: dict '''Lock config.''' def __init__( self, mangler, *, host: str = 'localhost', password: Optional[str] = None, port: int = 6379, db: int = 0, lockconf: Optional[dict] = None, **kwargs ): super().__init__(mangler) # Redis client creates a pool that connects lazily self.client = redis.StrictRedis(host, port, db, password, **kwargs) self._lockconf = lockconf or {} def lock(self, key: str) -> Lock: return Lock(self.mangler.nameLock(key), self.client, **self._lockconf) ``` -------------------------------- ### hermes.backend.inprocess.Backend Source: https://hermescache.readthedocs.io/en/latest/reference.html Simple in-process dictionary-based backend implementation. ```APIDOC ## hermes.backend.inprocess.Backend ### Description Simple in-process dictionary-based backend implementation. ### Constructor `Backend(mangler, *args)` ### Parameters - **mangler** - The key mangler to use. - **\*args** - Additional positional arguments. ``` -------------------------------- ### hermes.backend.inprocess.AsyncBackend Source: https://hermescache.readthedocs.io/en/latest/reference.html Simple in-process dictionary-based backend for asyncio programs. ```APIDOC ## hermes.backend.inprocess.AsyncBackend ### Description Simple in-process dictionary-based backend for `asyncio` programs. ### Constructor `AsyncBackend(mangler, *args)` ### Parameters - **mangler** - The key mangler to use. - **\*args** - Additional positional arguments. ``` -------------------------------- ### Cache class __init__ method Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Constructor for the Cache class, initializing TTL, mangler, and backend. Warns if backend options are passed with an instance. ```python def __init__( self, backend: Union[Type[AbstractBackend], AbstractBackend] = AbstractBackend, *, mangler: Optional[Mangler] = None, cachedfactory: Callable[..., Cached] = cachedfactory, ttl: int = 3600, **backendconf ): self.ttl = ttl mangler = mangler or Mangler() assert isinstance(mangler, Mangler) self.mangler = mangler if isinstance(backend, AbstractBackend): if backendconf: warnings.warn('Backend options ignored because backend instance is passed') ``` -------------------------------- ### BaseBackend.dump Source: https://hermescache.readthedocs.io/en/latest/reference.html Dumps the cache entries as a dictionary. ```APIDOC ## BaseBackend.dump ### Description Dumps the cache entries as a dictionary. ### Method dump ### Signature dump() ### Parameters None ### Returns Dict[str, Any] ``` -------------------------------- ### Backend.dump Source: https://hermescache.readthedocs.io/en/latest/reference.html Dumps the cache entries as a dictionary. ```APIDOC ## Backend.dump ### Description Dumps the cache entries as a dictionary. ### Method dump ### Signature dump() ### Parameters None ### Returns Dict[str, Any] ``` -------------------------------- ### AsyncLock.__exit__ Source: https://hermescache.readthedocs.io/en/latest/reference.html No-op context manager implementation used by hermes.Cached for synchronous code. ```APIDOC ## AsyncLock.__exit__ ### Description No-op context manager implementation. ### Method __exit__ ### Parameters - **args** - Required - Arguments passed to the exit method. ### Notes Used by `hermes.Cached` for synchronous code. ``` -------------------------------- ### BaseBackend.load Source: https://hermescache.readthedocs.io/en/latest/reference.html Loads cache entries by key or iterable of keys. For multiple keys, returns a dictionary excluding absent keys. ```APIDOC ## BaseBackend.load ### Description Loads cache entries by key or iterable of keys. For multiple keys, returns a dictionary excluding absent keys. ### Method load ### Signature load(keys) ### Parameters - **keys** (str | Iterable[str]) - Required - Key or iterable of keys to load. ### Returns Any | Dict[str, Any] | None ``` -------------------------------- ### AsyncBackend.clean Source: https://hermescache.readthedocs.io/en/latest/reference.html Purges the backend storage. ```APIDOC ## AsyncBackend.clean ### Description Purge the backend storage. ### Method clean ``` -------------------------------- ### Run cache.clean in asyncio thread pool Source: https://hermescache.readthedocs.io/en/latest/asyncio.html Shows how to run the synchronous Hermes.clean method in the default asyncio thread pool using loop.run_in_executor, as clean can be slow depending on the backend. ```python import asyncio loop = asyncio.get_event_loop() await loop.run_in_executor(None, cache.clean, ['pypi']) ``` -------------------------------- ### hermes.backend.AbstractBackend Source: https://hermescache.readthedocs.io/en/latest/reference.html Base backend class and no-op implementation. Users can subclass this to create custom cache backends. Provides methods for locking, saving, loading, removing, and cleaning cache entries. ```APIDOC ## Class: hermes.backend.AbstractBackend ### Description Base backend class. It's also the no-op implementation. Subclass this to implement a custom cache backend. ### Constructor `AbstractBackend(mangler)` #### Parameters - **mangler** (Mangler) - Required - Key manager responsible for creating keys, hashing and serialisation. ### Attributes - **mangler** (Mangler) - Key manager responsible for creating keys, hashing and serialisation. ### Methods #### lock(key) Create lock object for the key. ##### Parameters - **key** (str) - Required - The cache key. ##### Return Type - AbstractLock #### save(mapping, *, ttl=None) Save cache entry. ##### Parameters - **mapping** (Dict[str, Any]) - Required - `key`-`value` mapping for a bulk save. - **ttl** (int | None) - Optional - Cache entry time-to-live. #### load(keys) Load cache entry(ies). Note, when handling a multiple key call, absent value keys should be excluded from resulting dictionary. ##### Parameters - **keys** (str | Iterable[str]) - Required - Key or keys to load. ##### Return Type - Any | Dict[str, Any] | None #### remove(keys) Remove given keys. ##### Parameters - **keys** (str | Iterable[str]) - Required - Key or keys to remove. #### clean() Purge the backend storage. ``` -------------------------------- ### AsyncBackend.save Source: https://hermescache.readthedocs.io/en/latest/reference.html Saves cache entries in bulk. Accepts a mapping of keys to values and an optional TTL. ```APIDOC ## AsyncBackend.save ### Description Save cache entry. ### Method save ### Parameters - **mapping** (Dict[str, Any]) - Required - key-value mapping for a bulk save. - **ttl** (int | None) - Optional - Cache entry time-to-live. ``` -------------------------------- ### hermes.backend.redis.Backend Source: https://hermescache.readthedocs.io/en/latest/reference.html Redis backend implementation for Hermes Cache. ```APIDOC ## hermes.backend.redis.Backend ### Description Redis backend implementation for Hermes Cache. ### Constructor `Backend(mangler, *args, **kwargs)` ### Parameters - **mangler** - The key mangler to use. - **\*args** - Additional positional arguments. - **\*\*kwargs** - Additional keyword arguments. ``` -------------------------------- ### Backend.save Source: https://hermescache.readthedocs.io/en/latest/reference.html Saves cache entries in bulk with optional TTL. Overrides BaseBackend.save. ```APIDOC ## Backend.save ### Description Saves cache entries in bulk with optional TTL. Overrides BaseBackend.save. ### Method save ### Signature save(mapping, *, ttl=None) ### Parameters - **mapping** (Dict[str, Any]) - Required - key-value mapping for a bulk save. - **ttl** (int | None) - Optional - Cache entry time-to-live. ### Returns None ``` -------------------------------- ### hermes.backend.memcached.Backend Source: https://hermescache.readthedocs.io/en/latest/reference.html Memcached backend implementation for Hermes Cache. ```APIDOC ## hermes.backend.memcached.Backend ### Description Memcached backend implementation for Hermes Cache. ### Constructor `Backend(mangler, *args)` ### Parameters - **mangler** - The key mangler to use. - **\*args** - Additional positional arguments. ``` -------------------------------- ### Hermes Constructor Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Creates a Hermes cache instance. The backend argument is required, while mangler, cachedfactory, and ttl are optional. If backend is omitted, a no-op backend is used. ```APIDOC ## Hermes Constructor ### Description Creates a Hermes cache instance. The backend argument is required, while mangler, cachedfactory, and ttl are optional. If backend is omitted, a no-op backend is used. ### Method Constructor ### Signature `Hermes(backend, mangler=None, cachedfactory=None, ttl=None)` ### Parameters - **backend** (type) - Required - The cache backend class or instance. - **mangler** (type) - Optional - Typically a subclass of mangler instance. - **cachedfactory** (type) - Optional - A cache-point factory for functions and coroutines. - **ttl** (type) - Optional - Default cache entry time-to-live. ### Usage Example ```python import hermes.backend.redis cache = hermes.Hermes( hermes.backend.redis.Backend, ttl = 600, host = 'localhost', db = 1 ) @cache def foo(a, b): return a * b class Example: @cache(tags = ('math', 'power'), ttl = 1200) def bar(self, a, b): return a ** b @cache( tags = ('math', 'avg'), key = lambda fn, *args, **kwargs: 'avg:{0}:{1}'.format(*args), ) def baz(self, a, b): return (a + b) / 2.0 print(foo(2, 333)) example = Example() print(example.bar(2, 10)) print(example.baz(2, 10)) ``` ``` -------------------------------- ### nameEntry docstring with cache key representation Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Docstring for the nameEntry method explaining how callable fn is represented in the cache key: MethodType as (module, class, method), FunctionType as (module, function), and other callables with __name__ as (module, object). ```python def nameEntry(self, fn: Callable, *args, **kwargs) -> str: ''' Return cache key for given callable and its positional and keyword arguments. Note how callable, ``fn``, is represented in the cache key: 1) a ``types.MethodType`` instance -> names of ``(module, class, method)`` 2) a ``types.FunctionType`` instance -> names of ``(module, function)`` 3) other callalbe objects with ``__name__`` -> name of ``(module, object)`` This means that if two function are defined dynamically in the same module with same names, like:: ``` -------------------------------- ### BaseBackend.clean Source: https://hermescache.readthedocs.io/en/latest/reference.html Purges the backend storage, removing all cache entries. ```APIDOC ## BaseBackend.clean ### Description Purges the backend storage, removing all cache entries. ### Method clean ### Signature clean() ### Parameters None ### Returns None ``` -------------------------------- ### Backend.clean Source: https://hermescache.readthedocs.io/en/latest/reference.html Purges the backend storage, removing all cache entries. ```APIDOC ## Backend.clean ### Description Purges the backend storage, removing all cache entries. ### Method clean ### Signature clean() ### Parameters None ### Returns None ``` -------------------------------- ### Custom Compressor Source: https://hermescache.readthedocs.io/en/latest/customisation.html Shows how to disable compression or use a different compression algorithm by setting the compressor attribute on the mangler. ```APIDOC ## Custom Compressor ### Description Disable compression or use a different compression algorithm by setting the compressor attribute on the mangler. ### Usage ```python cache = hermes.Hermes(hermes.backend.redis.Backend) # disable compression cache.mangler.compressor = None # use another compression algorithm cache.mangler.compressor = hermes.Compressor( snappy.compress, snappy.decompress, snappy.UncompressError, 32 ) ``` ``` -------------------------------- ### BaseBackend.save Source: https://hermescache.readthedocs.io/en/latest/reference.html Saves cache entries in bulk using a key-value mapping. Optionally accepts a TTL for the entries. ```APIDOC ## BaseBackend.save ### Description Saves cache entries in bulk using a key-value mapping. Optionally accepts a TTL for the entries. ### Method save ### Signature save(mapping, *, ttl=None) ### Parameters - **mapping** (Dict[str, Any]) - Required - key-value mapping for a bulk save. - **ttl** (int | None) - Optional - Cache entry time-to-live. ### Returns None ``` -------------------------------- ### Lock class with acquire and release methods Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_0a3d1c31add3250f_redis_py.html The Lock class uses a Redis client to implement a distributed lock. The acquire method attempts to set a key with the value 'locked' using the nx and ex options, returning True on success. If wait is False, it returns False immediately; otherwise, it sleeps for the configured sleep interval and retries. The release method deletes the key. ```python def acquire(self, wait = True): while True: if self.client.set(self.key, 'locked', nx = True, ex = self.timeout): return True elif not wait: return False else: time.sleep(self.sleep) def release(self): self.client.delete(self.key) ``` -------------------------------- ### Custom Serialiser Source: https://hermescache.readthedocs.io/en/latest/customisation.html Shows how to replace the default serialisation algorithm by assigning a new Serialiser instance to the cache's mangler. ```APIDOC ## Custom Serialiser ### Description Replace the default serialisation algorithm by assigning a new Serialiser instance to the cache's mangler. ### Usage ```python cache = hermes.Hermes(hermes.backend.redis.Backend) # use another serialisation algorithm cache.mangler.serialiser = hermes.Serialiser( msgpack.dumps, lambda b: msgpack.loads(b, strict_map_key = False) ) ``` ``` -------------------------------- ### AsyncBackend.lock Source: https://hermescache.readthedocs.io/en/latest/reference.html Creates a lock object for the given key. Returns an AsyncLock instance. ```APIDOC ## AsyncBackend.lock ### Description Create lock object for the key. ### Method lock ### Parameters - **key** (str) - Required - The key for which to create the lock. ### Return Type AsyncLock ``` -------------------------------- ### Custom Mangler Source: https://hermescache.readthedocs.io/en/latest/customisation.html Shows how to create a custom Mangler subclass to override the hash algorithm used for cache keys. ```APIDOC ## Custom Mangler ### Description Create a custom Mangler subclass to override the hash algorithm used for cache keys. ### Usage ```python class CustomMangler(hermes.Mangler): def hash(self, value): return str(hash(value)) cache = hermes.Hermes( hermes.backend.redis.Backend, mangler = CustomMangler() ) ``` ``` -------------------------------- ### hermes.Compressor Source: https://hermescache.readthedocs.io/en/latest/reference.html Compression delegate. Holds compress and decompress callables, decompression error types, and minimum compression length. ```APIDOC ## hermes.Compressor ### Description Compression delegate. Holds compress and decompress callables, decompression error types, and minimum compression length. ### Constructor Parameters - compress (Callable[[bytes], bytes]) - Required - Function to compress serialised cache value. - decompress (Callable[[bytes], bytes]) - Required - Function to decompress serialised cache value. - decompressError (Type[Exception] | Tuple[Type[Exception], ...]) - Required - Exception type(s) that indicate uncompressed payload. - compressMinLength (int) - Optional - Minimum length for compression to be applied. Default is 0. ### Attributes - compress (Callable[[bytes], bytes]) - Compress serialised cache value. - decompress (Callable[[bytes], bytes]) - Decompress serialised cache value. - decompressError (Type[Exception] | Tuple[Type[Exception], ...]) - Decompression error(s) that indicate uncompressed payload. - compressMinLength (int) - Minimum length for compression. ``` -------------------------------- ### Backend.lock Source: https://hermescache.readthedocs.io/en/latest/reference.html Creates a lock object for the given key. ```APIDOC ## Backend.lock ### Description Creates a lock object for the given key. ### Method lock ### Signature lock(key) ### Parameters - **key** (str) - Required - The key for which to create a lock. ### Returns Lock ``` -------------------------------- ### Lock.acquire Source: https://hermescache.readthedocs.io/en/latest/reference.html Acquires the reentrant lock. ```APIDOC ## Lock.acquire ### Description Acquires the reentrant lock. ### Method acquire ### Signature acquire(wait=True) ### Parameters - **wait** (bool) - Optional - Whether to wait for the lock if it is held. Defaults to True. ### Returns None ``` -------------------------------- ### Hermes Source: https://hermescache.readthedocs.io/en/latest/reference.html The Hermes class is the main cache facade providing a unified interface to the caching system. ```APIDOC ## Hermes ### Description The Hermes class is the cache façade, providing the main entry point for caching operations. ### Constructor `Hermes(backend=None, mangler=None, cachedfactory=None, ttl=None)` ### Parameters - **backend** (optional) - The backend to use for caching. - **mangler** (optional) - The key mangler to use. - **cachedfactory** (optional) - The cached factory to use. - **ttl** (optional) - Default time-to-live for cached items. ``` -------------------------------- ### AsyncLock.acquire Source: https://hermescache.readthedocs.io/en/latest/reference.html Acquires the asynchronous lock. Used by CachedCoro for asynchronous code. ```APIDOC ## AsyncLock.acquire ### Description Acquire the asynchronous lock. ### Method acquire ### Parameters - **wait** (bool) - Optional - Whether to wait for the lock. Defaults to True. ### Return Type bool ### Notes Used by `CachedCoro` for asynchronous code. ``` -------------------------------- ### hermes.backend.redis.Backend Source: https://hermescache.readthedocs.io/en/latest/reference.html Redis backend implementation for Hermes Cache. Provides methods to save, load, remove, and clean cache entries, and to create lock objects. ```APIDOC ## Class: hermes.backend.redis.Backend ### Description Redis backend implementation for Hermes Cache. ### Constructor `Backend(mangler, *, host='localhost', password=None, port=6379, db=0, lockconf=None, **kwargs)` #### Parameters - **mangler** (callable) - Required - Mangler function. - **host** (str) - Optional - Redis host. Default: 'localhost'. - **password** (str | None) - Optional - Redis password. - **port** (int) - Optional - Redis port. Default: 6379. - **db** (int) - Optional - Redis database index. Default: 0. - **lockconf** (dict | None) - Optional - Lock configuration. - **kwargs** - Additional keyword arguments. ### Attributes - **client** (Redis) - Redis client. ### Methods #### lock(key) Create lock object for the key. **Parameters:** - **key** (str) - Required - Key for the lock. **Return type:** Lock #### save(mapping, *, ttl=None) Save cache entry. **Parameters:** - **mapping** (Dict[str, Any]) - Required - `key`-`value` mapping for a bulk save. - **ttl** (int | None) - Optional - Cache entry time-to-live. #### load(keys) Load cache entry(ies). Note, when handling a multiple key call, absent value keys should be excluded from resulting dictionary. **Parameters:** - **keys** (str | Iterable[str]) - Required - Key or keys to load. **Return type:** Any | Dict[str, Any] | None #### remove(keys) Remove given keys. **Parameters:** - **keys** (str | Iterable[str]) - Required - Key or keys to remove. #### clean() Purge the backend storage. ``` -------------------------------- ### Mangler.nameEntry method implementation Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Constructs a cache key for a callable object by combining prefix, module, class name, function name, and hashed arguments. Raises HermesError if the callable lacks a name or is not callable. ```python result = [self.prefix, 'entry'] if callable(fn): try: # types.MethodType result.extend([ fn.__module__, fn.__self__.__class__.__name__, # type: ignore[attribute-error] fn.__name__, ]) except AttributeError: try: # types.FunctionType and other object with __name__ result.extend([fn.__module__, fn.__name__]) except AttributeError: raise HermesError( 'fn is callable but its name is undefined, consider overriding Mangler.nameEntry' ) else: raise HermesError('fn is expected to be callable') arguments = args, tuple(sorted(kwargs.items())) result.append(self.hash(self.dumps(arguments))) return ':'.join(result) ``` -------------------------------- ### hermes.Mangler.nameTag Source: https://hermescache.readthedocs.io/en/latest/reference.html Builds a fully qualified backend tag name. ```APIDOC ## hermes.Mangler.nameTag ### Description Builds a fully qualified backend tag name. ### Method nameTag ### Parameters - **tag** (str) - Required - The tag name. ### Returns - **str** - The fully qualified backend tag name. ``` -------------------------------- ### Decompression and deserialization with error handling Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Attempts to decompress a value using the compressor, ignoring decompressError which indicates the value is shorter than compressMinLength, then deserializes the value. ```python try: value = self.compressor.decompress(value) except self.compressor.decompressError: # It is expected that the error indicates that the value is # shorter than compressMinLength pass return self.serialiser.loads(value) ``` -------------------------------- ### Custom Cached Source: https://hermescache.readthedocs.io/en/latest/customisation.html Shows how to customise the interpretation of cache operation failure by subclassing Cached and overriding __call__. ```APIDOC ## Custom Cached ### Description Customise the interpretation of cache operation failure by subclassing Cached and overriding __call__. ### Usage ```python class CustomCached(hermes.Cached): def __call__(self, *args, **kwargs): try: return super().__call__(*args, **kwargs) except redis.RedisError: return self._callable(*args, **kwargs) cache = hermes.Hermes( hermes.backend.redis.Backend, cachedcls = CustomCached ) ``` ``` -------------------------------- ### hermes.backend.AbstractLock Source: https://hermescache.readthedocs.io/en/latest/reference.html Base locking class. Implements context manager protocol. Mocks `acquire` and `release` i.e. it always acquires. Subclass this to implement a custom distributed lock. ```APIDOC ## Class: hermes.backend.AbstractLock ### Description Base locking class. Implements context manager protocol. Mocks `acquire` and `release` i.e. it always acquires. Subclass this to implement a custom distributed lock. ### Constructor `AbstractLock(key)` #### Parameters - **key** (str) - Required - The cache key. ### Attributes - **key** (str) - Implementation may be key-aware. ### Methods #### __enter__() Enter context manager by acquiring the distributed lock. #### __exit__(type, value, traceback) Exit context manager by releasing the distributed lock. #### acquire(wait=True) Acquire distributed lock. ##### Parameters - **wait** (bool) - Optional - Whether to wait for the lock. ##### Returns - bool - Whether the lock was acquired. #### release() Release distributed lock. ``` -------------------------------- ### Hermes.clean Source: https://hermescache.readthedocs.io/en/latest/reference.html Clean all, or tagged with given tags, cache entries. If tags argument is omitted, flushes all cache entries; otherwise only entries tagged by given tags are flushed. ```APIDOC ## Hermes.clean ### Description Clean all, or tagged with given tags, cache entries. If this argument is omitted the call flushes all cache entries, otherwise only the entries tagged by given tags are flushed. ### Method clean ### Signature clean(tags=()) ### Parameters - **tags** (Sequence[str]) - Optional - If this argument is omitted the call flushes all cache entries, otherwise only the entries tagged by given tags are flushed. ### Usage Example ```python cache.clean(['math']) # invalidate entries tagged 'math' cache.clean() # flush cache ``` ``` -------------------------------- ### memcached.py: Lock class implementation Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_0a3d1c31add3250f_memcached_py.html Defines a distributed lock using Memcached. The lock uses a key and a client, with configurable sleep and timeout. The acquire method uses client.add to set the lock and returns True if successful, or waits if wait is True. ```python import time from typing import Any, Dict, Iterable, Optional, Union import pymemcache from . import AbstractBackend, AbstractLock __all__ = 'Backend', class Lock(AbstractLock): '''Key-aware distributed lock.''' client: pymemcache.PooledClient '''Memcached client.''' timeout: int ''' Maximum TTL of lock, can be up to 30 days, otherwise memcached will treated it as a UNIX timestamp of an exact date. ''' sleep: float '''Amount of time to sleep per ``while True`` iteration when waiting.''' def __init__( self, key: str, client: pymemcache.PooledClient, *, sleep: float = 0.1, timeout: int = 900 ): super().__init__(key) self.client = client self.sleep = sleep self.timeout = timeout def acquire(self, wait = True): while True: if self.client.add(self.key, 'locked', self.timeout, noreply = False): return True elif not wait: return False time.sleep(self.sleep) ``` -------------------------------- ### hermes.Serialiser Source: https://hermescache.readthedocs.io/en/latest/reference.html Serialisation delegate. Holds dumps and loads callables for serialising and deserialising cache values. ```APIDOC ## hermes.Serialiser ### Description Serialisation delegate. Holds dumps and loads callables for serialising and deserialising cache values. ### Constructor Parameters - dumps (Callable[[Any], bytes]) - Required - Function to serialise cache value. - loads (Callable[[bytes], Any]) - Required - Function to deserialise cache value. ### Attributes - dumps (Callable[[Any], bytes]) - Serialise cache value. - loads (Callable[[bytes], Any]) - Deserialise cache value. ``` -------------------------------- ### Backend.stopWatch Source: https://hermescache.readthedocs.io/en/latest/reference.html Stops the TTL watch thread and joins it. ```APIDOC ## Backend.stopWatch ### Description Stops the TTL watch thread and joins it. ### Method stopWatch ### Signature stopWatch() ### Parameters None ### Returns None ``` -------------------------------- ### Lock.release Source: https://hermescache.readthedocs.io/en/latest/reference.html Releases the reentrant lock. ```APIDOC ## Lock.release ### Description Releases the reentrant lock. ### Method release ### Signature release() ### Parameters None ### Returns None ``` -------------------------------- ### hermes.Mangler.nameLock Source: https://hermescache.readthedocs.io/en/latest/reference.html Creates a fully qualified backend lock key for an entry key. ```APIDOC ## hermes.Mangler.nameLock ### Description Creates a fully qualified backend lock key for the entry key. If the entry key is already a colon-separated key name with first component equal to the prefix, the first two components are dropped. ### Method nameLock ### Parameters - **entryKey** (str) - Required - Entry key to create a lock key for. ### Returns - **str** - The fully qualified backend lock key. ``` -------------------------------- ### hermes.backend.memcached.Backend Source: https://hermescache.readthedocs.io/en/latest/reference.html Memcached backend implementation. Provides methods to lock, save, load, remove, and clean cache entries. ```APIDOC ## Class: hermes.backend.memcached.Backend ### Description Memcached backend implementation. ### Constructor `Backend(mangler, *, server='localhost:11211', lockconf=None, **kwargs)` #### Parameters - **mangler** (object) - Required - Mangler instance. - **server** (str) - Optional - Memcached server address. Default: 'localhost:11211'. - **lockconf** (dict | None) - Optional - Lock configuration. - **kwargs** - Optional - Additional keyword arguments. ### Attributes - **client** (PooledClient) - Memcached client. ### Methods #### lock(key) Create lock object for the key. ##### Parameters - **key** (str) - Required - Key for the lock. ##### Returns - **Lock** - A Lock object. #### save(mapping, *, ttl=None) Save cache entry. ##### Parameters - **mapping** (Dict[str, Any]) - Required - `key`-`value` mapping for a bulk save. - **ttl** (int | None) - Optional - Cache entry time-to-live. #### load(keys) Load cache entry(ies). ##### Parameters - **keys** (str | Iterable[str]) - Required - Key or keys to load. ##### Returns - **Any | Dict[str, Any] | None** - Loaded value(s). When multiple keys are requested, absent keys are excluded from the result. #### remove(keys) Remove given keys. ##### Parameters - **keys** (str | Iterable[str]) - Required - Key or keys to remove. #### clean() Purge the backend storage. ``` -------------------------------- ### BaseBackend.remove Source: https://hermescache.readthedocs.io/en/latest/reference.html Removes the given keys from the cache. ```APIDOC ## BaseBackend.remove ### Description Removes the given keys from the cache. ### Method remove ### Signature remove(keys) ### Parameters - **keys** (str | Iterable[str]) - Required - Key or iterable of keys to remove. ### Returns None ``` -------------------------------- ### hermes.Mangler.dumps Source: https://hermescache.readthedocs.io/en/latest/reference.html Serialises and conditionally compresses a value. ```APIDOC ## hermes.Mangler.dumps ### Description Serialises and conditionally compresses a value. ### Method dumps ### Returns - **bytes** - The serialised (and possibly compressed) value. ``` -------------------------------- ### hermes.Mangler.loads Source: https://hermescache.readthedocs.io/en/latest/reference.html Conditionally decompresses and deserialises a value. ```APIDOC ## hermes.Mangler.loads ### Description Conditionally decompresses and deserialises a value. ### Method loads ### Parameters - **value** (bytes) - Required - The value to deserialise. ### Returns - The deserialised value. ``` -------------------------------- ### Cached.__get__ descriptor implementation Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Implements the non-data descriptor protocol. It locks the backend, reads the value, and if missing, calls the decorated function and saves the result. The comment notes it's better to read twice than lock every read. ```python if value is None: with self._frontend.backend.lock(key): # it's better to read twice than lock every read value = self._load(key) if value is None: value = self._callable(*args, **kwargs) self._save(key, value) return value ``` -------------------------------- ### Cache class attribute definitions Source: https://hermescache.readthedocs.io/en/report/htmlcov/z_29e638db6e54f801___init___py.html Attribute definitions for the Cache class: backend, mangler, cachedfactory, and ttl. ```python backend: AbstractBackend ``` ```python '''Cache backend.''' ``` ```python mangler: Mangler ``` ```python '''Key manager responsible for creating keys, hashing and serialisation.''' ``` ```python cachedfactory: Callable[..., Cached] ``` ```python '''Cache-point callable object factory.''' ``` ```python ttl: int ``` ```python '''Default cache entry time-to-live.''' ``` -------------------------------- ### AsyncLock.release Source: https://hermescache.readthedocs.io/en/latest/reference.html Releases the asynchronous lock. Used by CachedCoro for asynchronous code. ```APIDOC ## AsyncLock.release ### Description Release the asynchronous lock. ### Method release ### Notes Used by `CachedCoro` for asynchronous code. ```