privex.helpers.cache

Helper functions/classes related to caching.

This module acts as a singleton wrapper, allowing for easily setting a framework-independent global cache API.

To make the module easy to use, adapter_get() initialises an instance of MemoryCache if no global cache adapter instance has been setup. This means you can use the various alias functions in this module without having to configure a cache adapter.

Available Cache Adapters

Standard Synchronous Adapters

Two synchronous cache adapters are included by default - MemoryCache (dependency free), and RedisCache (needs redis library).

While these synchronous classes don’t support coroutines for most methods, as of privex-helpers 2.7 the method privex.helpers.cache.CacheAdapter.CacheAdapter.get_or_set_async() is an async version of CacheAdapter.get_or_set(), and is available on all CacheAdapter sub-classes (both MemoryCache and RedisCache). get_or_set_async allows a coroutine or coroutine function/method reference to be passed as the fallback value.

Adapter

Description

CacheAdapter

This is the base class for all synchronous cache adapters (doesn’t do anything)

MemoryCache

A cache adapter which stores cached items in memory using a dict. Fully functional incl. timeout.

RedisCache

A cache adapter for Redis using the python library redis

Asynchronous (Python AsyncIO) Adapters

Over the past few years, Python’s AsyncIO has grown more mature and has gotten a lot of attention. Thankfully, whether you use AsyncIO or not, we’ve got you covered.

Three AsyncIO cache adapters are included by default - AsyncMemoryCache (dependency free), AsyncRedisCache (needs aioredis library), and AsyncMemcachedCache (needs aiomcache library).

Adapter

Description

AsyncCacheAdapter

This is the base class for all AsyncIO cache adapters (abstract class, only implements get_or_set)

AsyncMemoryCache

A cache adapter which stores cached items in memory using a dict. Fully functional incl. timeout.

AsyncRedisCache

A cache adapter for Redis using the AsyncIO python library aioredis

AsyncMemcachedCache

A cache adapter for Memcached using the AsyncIO python library aiomcache

Setting / updating the global cache adapter instance

First import the cache module.

>>> from privex.helpers import cache

You must instantiate your cache adapter of choice before passing it to adapter_set() - which updates the global cache adapter instance.

>>> my_adapter = cache.MemoryCache()
>>> cache.adapter_set(my_adapter)

Once you’ve set the adapter, you can use the module functions such as get() and set() - or you can import cached to enable dictionary-like cache item access.

>>> cache.set('hello', 'world')
>>> cache.get('hello')
'world'
>>> from privex.helpers import cached
>>> cached['hello']
'world'
>>> cached['otherkey'] = 'testing'

You can also use AsyncIO adapters with the global cache adapter wrapper. CacheWrapper uses awaitable() to ensure that AsyncIO adapters can work synchronously when being called from a synchronous function, while working asynchronously from a non-async function.

>>> my_adapter = cache.AsyncRedisCache()
>>> cache.adapter_set(my_adapter)
>>>
>>> # get_hello_async() is async, so @awaitable returns the normal .get() coroutine for awaiting
>>> async def get_hello_async():
...     result = await cached.get('hello')
...     return result
...
>>> # get_hello() is synchronous, so @awaitable seamlessly runs .get() in an event loop and returns
>>> # the result - get_hello() can treat it as if it were just a normal synchronous function.
>>> def get_hello():
...     return cached.get('hello')
...
>>> get_hello()
'world'
>>> await get_hello_async()
'world'

Plug-n-play usage

As explained near the start of this module’s documentation, you don’t have to set the global adapter if you only plan on using the simple MemoryCache adapter.

Just start using the global cache API via either privex.helpers.cache or privex.helpers.cache.cached and MemoryCache will automatically be instantiated as the global adapter as soon as something attempts to access the global instance.

We recommend importing cached rather than cache, as it acts as a wrapper that allows dictionary-like cache key getting/setting, and is also immediately aware when the global cache adapter is set/replaced.

>>> from privex.helpers import cached

You can access cached like a dictionary to get and set cache keys (they will use the default expiry time of privex.helpers.settings.DEFAULT_CACHE_TIMEOUT)

>>> cached['testing'] = 123
>>> cached['testing']
123

You can also call methods such as get() and set() for getting/setting cache items with more control, for example:

  1. Setting a custom expiration, or disabling expiration by setting timeout to None

    >>> cached.set('example', 'test', timeout=30)   # Drop 'example' from the cache after 30 seconds from now.
    >>> cached.set('this key', 'is forever!', timeout=None) # A timeout of ``None`` disables automatic expiration.
    
  2. Fallback values when a key isn’t found, or have it throw an exception if it’s not found instead.

    >>> cached.get('example', 'NOT FOUND')          # If the key 'example' doesn't exist, return 'NOT FOUND'
    'test'
    
    >>> try:   # By setting ``fail`` to True, ``get`` raises ``CacheNotFound`` if the key doesn't exist / is expired
    ...     cached.get('nonexistent', fail=True)
    ... except CacheNotFound:
    ...     log.error('The cache key "nonexistent" does not exist!')
    >>>
    
  3. Using get_or_set() you can specify either a standard type (e.g. str, int, dict), or even a custom function to call to obtain the value to set and return.

    >>> cached.get_or_set('hello', lambda key: 'world', timeout=60)
    >>> cached['hello']
    'world'
    

Copyright:

    +===================================================+
    |                 © 2019 Privex Inc.                |
    |               https://www.privex.io               |
    +===================================================+
    |                                                   |
    |        Originally Developed by Privex Inc.        |
    |        License: X11 / MIT                         |
    |                                                   |
    |        Core Developer(s):                         |
    |                                                   |
    |          (+)  Chris (@someguy123) [Privex]        |
    |          (+)  Kale (@kryogenic) [Privex]          |
    |                                                   |
    +===================================================+

Copyright 2019     Privex Inc.   ( https://www.privex.io )

Attributes

Functions

adapter_get([default])

Get the global cache adapter instance.

adapter_set(adapter)

Set the global cache adapter instance to adapter - which should be an instantiated adapter class which implements CacheAdapter

get(key[, default, fail])

Return the value of cache key key.

get_or_set(key, value[, timeout])

Attempt to return the value of key in the cache.

remove(*key)

Remove one or more keys from the cache.

set(key, value[, timeout])

Set the cache key key to the value value, and automatically expire the key after timeout seconds from now.

update_timeout(key[, timeout])

Update the timeout for a given key to datetime.utcnow() + timedelta(seconds=timeout)

Classes

CacheWrapper()

CacheWrapper is a small class designed to wrap an instance of CacheAdapter and allow the adapter to be switched out at any time, using the static class attribute cache_instance.