Customisation

The following functions, classes and delegates allow customisation of different details of how Hermes instance works.

Custom cache keys

Hermes.__call__(*args[, ttl, tags, key])

Wrap the callable in a cache-point instance.

In rare cases like metaprogramming Mangler.nameEntry cannot determine correct and unique cache key. In such cases a function to compute the cache key name can be passed to the decorator:

cache = hermes.Hermes(hermes.backend.redis.Backend)

@cache(key = lambda fn, a: f'{repr(fn).rsplit(" at 0x", 1)[0]}:{a}')
@magic_metaprogramming_sauce
def fn(a):
  return a + 1

Dynamic TTL

Hermes.__call__(*args[, ttl, tags, key])

Wrap the callable in a cache-point instance.

Usually TTL of cache entries is a fixed number (of seconds). However, to support custom intervals, like time until midnight, or caching a class of return values of a cached function longer, ttl argument of the decorator can be a callable:

cache = hermes.Hermes(hermes.backend.redis.Backend)

@cache(ttl = lambda ret_val, get_foo, id: 7200 if ret_val.is_longlived else 3600)
def get_foo(id):
  return foo_repo.get(id)

Hermes.ttl can be set to a callable too, hence TTL can be managed centrally, if needed.

Hash algorithm

Mangler()

Key manager responsible for creating keys, hashing and serialisation.

class CustomMangler(hermes.Mangler):

  def hash(self, value):
    return str(hash(value))

cache = hermes.Hermes(
  hermes.backend.redis.Backend, mangler = CustomMangler()
)

Serialisation

Serialiser(dumps, loads)

Serialisation delegate.

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)
)

Сompression

Compressor(compress, decompress, decompressError)

Compression delegate.

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
)

Tolerating cache server errors

Cached(frontend, callable, *[, ttl, key, tags])

Cache-point wrapper for callables and descriptors.

CachedCoro(frontend, callable, *[, ttl, ...])

Cache-point wrapper for coroutine functions.

This can be used to ignore backend errors when the cache server is down by falling back to the cached callable directly.

class CustomCached(hermes.Cached):

  def __call__(self, *args, **kwargs):
    try:
      return super().__call__(*args, **kwargs)
    except redis.RedisError:
      return self._callable(*args, **kwargs)

def cachedfactory(frontend: hermes.Hermes, fn, **kwargs) -> hermes.Cached:
  if callable(fn) or inspect.ismethoddescriptor(fn):
    return CustomCached(frontend, fn, **kwargs)
  else:
    raise HermesError(
      'First positional argument must be a callable or method descriptor'
    )

cache = hermes.Hermes(
  hermes.backend.redis.Backend, cachedfactory = cachedfactory
)