Reference¶
The following classes are the main API of the package.
|
Cache façade. |
|
Redis backend implementation. |
|
Memcached backend implementation. |
|
Simple in-process dictionary-based backend implementation. |
|
Simple in-process dictionary-based backend for |
Facade¶
- class hermes.Hermes(backend=AbstractBackend, *, mangler=None, cachedfactory=cachedfactory, ttl=3600, **backendconf)¶
Cache façade.
- Parameters:
backend (AbstractBackend) –
Class or instance of cache backend. If a class is passed, keyword arguments of passed to
Hermes
constructor will be bypassed to the class’ constructor.If the argument is omitted no-op backend will be be used.
mangler (Mangler) – Optional, typically of a subclass, mangler instance.
cachedfactory (Callable[[...], Cached]) – Optional, a cache-point factory for functions and coroutines.
ttl (int | Callable[[...], int]) – Default cache entry time-to-live.
backend
mangler
cachedfactory
ttl
Usage:
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)) 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
- ttl: int | Callable[[...], int]¶
Default cache entry time-to-live.
It can be either a number of seconds, or a function to calculate it. The latter is given:
the return value of the decorated callable’s call
the decorated callable object
actual positional arguments of the call
actual keyword arguments of the call
- backend: AbstractBackend¶
Cache backend.
- __call__(*args, ttl=None, tags=(), key=None)¶
Wrap the callable in a cache-point instance.
Decorator that caches method or function result. The following key arguments are optional:
Bare decorator,
@cache
, is supported as well as a call with keyword arguments@cache(ttl = 7200)
.- Parameters:
ttl (int | Callable[[...], int] | None) – Cache entry Time To Live. See
ttl
.tags (Sequence[str]) – Cache entry tag list.
key (Callable[[...], str] | None) – Lambda that provides custom key, otherwise
Mangler.nameEntry
is used.ttl
tags
key
- clean(tags=())¶
Clean all, or tagged with given tags, cache entries.
- Parameters:
tags (Sequence[str]) – If this argument is omitted the call flushes all cache entries, otherwise only the entries tagged by given tags are flushed.
tags
- class hermes.HermesError¶
Generic Hermes error.
Redis backend¶
- class hermes.backend.redis.Backend(mangler, *, host='localhost', password=None, port=6379, db=0, lockconf=None, **kwargs)¶
Bases:
AbstractBackend
Redis backend implementation.
- Parameters:
host (str)
password (str | None)
port (int)
db (int)
lockconf (dict | None)
- client: Redis¶
Redis client.
- save(mapping, *, ttl=None)¶
Save cache entry.
- Parameters:
mapping (Dict[str, Any]) –
key
-value
mapping for a bulk save.ttl (int | None) – Cache entry time-to-live .
mapping
ttl
- 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])
- Return type:
Any | Dict[str, Any] | None
- remove(keys)¶
Remove given keys.
- Parameters:
keys (str | Iterable[str])
- clean()¶
Purge the backend storage.
- class hermes.backend.redis.Lock(key, client, *, sleep=0.1, timeout=900)¶
Bases:
AbstractLock
Key-aware distributed lock. “Distributed” is in sense of clients, not Redis instances. Implemented as described in Correct implementation with a single instance, but without setting unique value to the lock entry and later checking it, because it is expected for a cached function to complete before lock timeout.
- Parameters:
key (str)
client (Redis)
sleep (float)
timeout (int)
- client: Redis¶
Redis client.
- sleep: float¶
Amount of time to sleep per
while True
iteration when waiting.
- timeout: int¶
Maximum TTL of lock.
- acquire(wait=True)¶
Acquire distributed lock.
- Parameters:
wait – Whether to wait for the lock.
- Returns:
Whether the lock was acquired.
- release()¶
Release distributed lock.
Memcached backend¶
- class hermes.backend.memcached.Backend(mangler, *, server='localhost:11211', lockconf=None, **kwargs)¶
Bases:
AbstractBackend
Memcached backend implementation.
- Parameters:
server (str)
lockconf (dict | None)
- client: PooledClient¶
Memcached client.
- save(mapping, *, ttl=None)¶
Save cache entry.
- Parameters:
mapping (Dict[str, Any]) –
key
-value
mapping for a bulk save.ttl (int | None) – Cache entry time-to-live .
mapping
ttl
- 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])
- Return type:
Any | Dict[str, Any] | None
- remove(keys)¶
Remove given keys.
- Parameters:
keys (str | Iterable[str])
- clean()¶
Purge the backend storage.
- class hermes.backend.memcached.Lock(key, client, *, sleep=0.1, timeout=900)¶
Bases:
AbstractLock
Key-aware distributed lock.
- Parameters:
key (str)
client (PooledClient)
sleep (float)
timeout (int)
- client: PooledClient¶
Memcached client.
- sleep: float¶
Amount of time to sleep per
while True
iteration when waiting.
- 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.
- acquire(wait=True)¶
Acquire distributed lock.
- Parameters:
wait – Whether to wait for the lock.
- Returns:
Whether the lock was acquired.
- release()¶
Release distributed lock.
In-process backend¶
- class hermes.backend.inprocess.BaseBackend(mangler)¶
Bases:
AbstractBackend
Base dictionary backend without key expiration.
- cache: dict¶
A
dict
used to store cache entries.
- save(mapping, *, ttl=None)¶
Save cache entry.
- Parameters:
mapping (Dict[str, Any]) –
key
-value
mapping for a bulk save.ttl (int | None) – Cache entry time-to-live .
mapping
ttl
- 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])
- Return type:
Any | Dict[str, Any] | None
- remove(keys)¶
Remove given keys.
- Parameters:
keys (str | Iterable[str])
- clean()¶
Purge the backend storage.
- dump()¶
Dump the cache entries. Sorry, Barbara.
- Return type:
Dict[str, Any]
- class hermes.backend.inprocess.Backend(mangler, *, ttlWatchSleep=1)¶
Bases:
BaseBackend
Simple in-process dictionary-based backend implementation.
In-process in-memory cache without memory limit, but with expiration. Besides testing, it may be suitable for limited number of real-world use-cases with a priori small cached data.
- Parameters:
ttlWatchSleep (float)
- save(mapping, *, ttl=None)¶
Save cache entry.
- Parameters:
mapping (Dict[str, Any]) –
key
-value
mapping for a bulk save.ttl (int | None) – Cache entry time-to-live .
mapping
ttl
- clean()¶
Purge the backend storage.
- stopWatch()¶
Ask TTL watch thread to stop and join it.
- dump()¶
Dump the cache entries. Sorry, Barbara.
- Return type:
Dict[str, Any]
- class hermes.backend.inprocess.Lock(key=None)¶
Bases:
AbstractLock
Key-unaware reentrant thread lock.
- Parameters:
key (str)
- acquire(wait=True)¶
Acquire the
RLock
.
- release()¶
Release the
RLock
.
- class hermes.backend.inprocess.AsyncBackend(mangler, *, ttlWatchSleep=1)¶
Bases:
BaseBackend
Simple in-process dictionary-based backend for
asyncio
programs.For cache entries to expire according to their TTL,
startWatch()
must be awaited manually when the IO loop is already running.- Parameters:
ttlWatchSleep (float)
- save(mapping, *, ttl=None)¶
Save cache entry.
- Parameters:
mapping (Dict[str, Any]) –
key
-value
mapping for a bulk save.ttl (int | None) – Cache entry time-to-live .
mapping
ttl
- clean()¶
Purge the backend storage.
- startWatch()¶
Start TTL watching task.
It must be called when
asyncio
IO loop is running.
- stopWatch()¶
Stop TTL watching task.
- class hermes.backend.inprocess.AsyncLock(key)¶
Bases:
AbstractLock
Key-aware asynchronous lock.
Note that instances of this class are used for both synchronous and asynchronous cases. For asynchronous cases
asyncio.Lock
is used per key. When a synchronous callable is cached in an asynchronous application, synchronous code is by definition executed serially in single-threaded Python process running anasyncio
IO loop. Hence, for synchronous code this class does nothing.The trick that makes it work for the both cases is that
hermes.Cached
uses the context manager protocol, andhermes.CachedCoro
usesacquire
andrelease
directly.- Parameters:
key (str)
- __enter__()¶
No-op context manager implementation.
Used by
hermes.Cached
for synchronous code.
- __exit__(*args)¶
No-op context manager implementation.
Used by
hermes.Cached
for synchronous code.
- async acquire(wait=True)¶
Acquire the asynchronous lock.
Used by
CachedCoro
for asynchronous code.- Return type:
bool
- async release()¶
Release the asynchronous lock.
Used by
CachedCoro
for asynchronous code.This method does not have to a coroutine itself, because underlying
release
is synchronous. But becausehermes.CachedCoro
runs regular synchronous callables in a thread pool, and the thread won’t have running IO loop, making this a coroutine lead to desired behaviour.
Extension & customisation¶
- class hermes.Mangler¶
Key manager responsible for creating keys, hashing and serialisation.
- prefix = 'cache'¶
Prefix for cache and tag entries.
- serialiser = (<built-in function dumps>, <built-in function loads>)¶
Serialisation delegate.
- compressor = (<built-in function compress>, <built-in function decompress>, <class 'zlib.error'>, 100)¶
Optional compression delegate.
- hash(value)¶
Hash value.
- Returns:
base64 encoded MD5 hash of the value.
- Parameters:
value (bytes)
- Return type:
str
- dumps(value)¶
Serialise and conditionally compress value.
- Return type:
bytes
- loads(value)¶
Conditionally decompress and deserialise value.
- Parameters:
value (bytes)
- nameEntry(fn, *args, **kwargs)¶
Return cache key for given callable and its positional and keyword arguments.
Note how callable,
fn
, is represented in the cache key:a
types.MethodType
instance -> names of(module, class, method)
a
types.FunctionType
instance -> names of(module, function)
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:
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))
Both will return 3, because cache keys will clash. In such cases you need to pass
key
with custom key function.It can also be that an object in case 3 doesn’t have a name, or its name isn’t unique, then a
nameEntry
should be overridden with something that represents it uniquely, likerepr(fn).rsplit(' at 0x', 1)[0]
(address should be stripped so after Python process restart the cache can still be valid and usable).- Parameters:
fn (Callable)
- Return type:
str
- nameTag(tag)¶
Build fully qualified backend tag name.
- Parameters:
tag (str)
- Return type:
str
- mapTags(tagKeys)¶
Map tags to random values for seeding.
- Parameters:
tagKeys (Iterable[str])
- Return type:
Dict[str, str]
- hashTags(tagMap)¶
Hash tags of a cache entry for the entry key,
- Parameters:
tagMap (Dict[str, str])
- Return type:
str
- nameLock(entryKey)¶
Create fully qualified backend lock key for the entry key.
- Parameters:
entryKey (str) –
Entry key to create a lock key for. If given entry key is already a colon-separated key name with first component equal to
prefix
, first to components are dropped. For instance:foo
→cache:lock:foo
cache:entry:fn:tagged:78d64ea049a57494
→cache:lock:fn:tagged:78d64ea049a57494
- Return type:
str
- class hermes.Cached(frontend, callable, *, ttl=None, key=None, tags=())¶
Cache-point wrapper for callables and descriptors.
- Parameters:
frontend (Hermes)
callable (Callable)
ttl (int | Callable[[...], int] | None)
key (Callable[[...], str] | None)
tags (Sequence[str])
- invalidate(*args, **kwargs)¶
Invalidate the cache entry.
Invalidated entry corresponds to the wrapped callable called with given
args
andkwargs
.
- __call__(*args, **kwargs)¶
Get the value of the wrapped callable.
- __get__(instance, type)¶
Implements non-data descriptor protocol.
The invocation happens only when instance method is decorated, so we can distinguish between decorated
types.MethodType
andtypes.FunctionType
. Python class declaration mechanics prevent a decorator from having awareness of the class type, as the function is received by the decorator before it becomes an instance method.How it works:
cache = hermes.Hermes() class Model: @cache def calc(self): return 42 m = Model() m.calc
Last attribute access results in the call,
calc.__get__(m, Model)
, wherecalc
is instance ofCached
which decorates the originalModel.calc
.Note, initially
Cached
is created on decoration per class method, when class type is created by the interpreter, and is shared among all instances. Later, on attribute access, a copy is returned with bound_callable
, just like ordinary Python method descriptor works.For more details, descriptor-protocol.
- class hermes.CachedCoro(frontend, callable, *, ttl=None, key=None, tags=())¶
Cache-point wrapper for coroutine functions.
The implementation uses the default thread pool of
asyncio
to execute synchronous functions of the cache backend, and manage their (distributed) locks.- Parameters:
frontend (Hermes)
callable (Callable)
ttl (int | Callable[[...], int] | None)
key (Callable[[...], str] | None)
tags (Sequence[str])
- async invalidate(*args, **kwargs)¶
Invalidate the cache entry.
Invalidated entry corresponds to the wrapped coroutine function called with given
args
andkwargs
.
- async __call__(*args, **kwargs)¶
Get the value of the wrapped coroutine function’s coroutine.
- class hermes.Serialiser(dumps, loads)¶
Serialisation delegate.
- Parameters:
dumps (Callable[[Any], bytes])
loads (Callable[[bytes], Any])
- dumps: Callable[[Any], bytes]¶
Serialise cache value.
- loads: Callable[[bytes], Any]¶
Deserialise cache value.
- class hermes.Compressor(compress, decompress, decompressError, compressMinLength=0)¶
Compression delegate.
- Parameters:
compress (Callable[[bytes], bytes])
decompress (Callable[[bytes], bytes])
decompressError (Type[Exception] | Tuple[Type[Exception], ...])
compressMinLength (int)
- 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¶
Minimal length of payload in bytes to trigger compression.
- class hermes.backend.AbstractBackend(mangler)¶
Base backend class. It’s also the no-op implementation.
- Parameters:
mangler (Mangler)
- lock(key)¶
Create lock object for the key.
- Parameters:
key (str)
- Return type:
- save(mapping, *, ttl=None)¶
Save cache entry.
- Parameters:
mapping (Dict[str, Any]) –
key
-value
mapping for a bulk save.ttl (int | None) – Cache entry time-to-live .
mapping
ttl
- 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])
- Return type:
Any | Dict[str, Any] | None
- remove(keys)¶
Remove given keys.
- Parameters:
keys (str | Iterable[str])
- clean()¶
Purge the backend storage.
- class hermes.backend.AbstractLock(key)¶
Base locking class. Implements context manger protocol. Mocks
acquire
andrelease
i.e. it always acquires.- Parameters:
key (str)
- key: str¶
Implementation may be key-aware.
- __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 – Whether to wait for the lock.
- Returns:
Whether the lock was acquired.
- Return type:
bool
- release()¶
Release distributed lock.