Usage

Overview

In one sentence, Bucket is a container object with optional lifetime backed by configurable serialisation methods that can also act as a function or method decorator.

Before everything is explained in detail, here’s a quick look at the functionality:

from bucketcache import Bucket

bucket = Bucket('cache', hours=1)

bucket[any_object] = anything_serializable_by_backend  # (Pickle is the default)
class SomeService(object):
    def __init__(self, username, password):
        self.username = username
        self.password = password

    @bucket(method=True, nocache='skip_cache')
    def expensive_method(self, a, b, c, skip_cache=False):
        print('Method called.')

    @expensive_method.callback
    def expensive_method(self, callinfo):
        print('Cache used.')

some_service = SomeService()
some_service.expensive_method(1, 2, 3)
some_service.expensive_method(1, 2, 3)
some_service.expensive_method(1, 2, 3, skip_cache=True)
Method called.
Cache used.
Method called.

Bucket Creation

Path

Bucket has one required parameter: path. This must be a str or Path directory for storing the cached files.

Lifetime

Lifetime refers to how long a key is readable from the bucket after setting it. By default, keys do not expire.

There are two methods of setting a lifetime:

from datetime import timedelta

bucket = Bucket('path', lifetime=timedelta(days=1, seconds=1, microseconds=1)))
bucket = Bucket('path', days=1, seconds=1, microseconds=1)

The latter is just a shortcut for the former. See datetime.timedelta for all supported keyword arguments.

Backends

Buckets can use any backend conforming to abstract class bucketcache.backends.Backend. There are three provided backends:

  • PickleBackend
  • JSONBackend
  • MessagePackBackend (if python-msgpack is installed)

By default, Pickle is used. Explicitly, this is specified as follows:

from bucketcache import PickleBackend

bucket = Bucket('path', backend=PickleBackend)

Each backend has an associated bucketcache.config.BackendConfig subclass.

For example, protocol version 4 could be used if on Python 3.4+

from bucketcache import PickleConfig

bucket = Bucket('path', backend=PickleBackend, config=PickleConfig(protocol=4))

Typically, all of the parameters that can be used by the relevant dump or load methods can be specified in a config object.

KeyMakers

Buckets can use any backend conforming to abstract class bucketcache.keymakers.KeyMaker. By default, DefaultKeyMaker is used, as it can convert almost any object into a key.

As DefaultKeyMaker converts objects to keys in memory, this can cause problems with large key objects. StreamingDefaultKeyMaker can be used instead, which uses a temporary file behind the scenes to reduce memory usage.

from bucketcache import StreamingDefaultKeyMaker

bucket = Bucket('path', keymaker=StreamingDefaultKeyMaker())

Decorator

Functions and methods

@bucket
def function(a, b):
    ...

class A(object):
    @bucket(method=True)
    def method(self, a, b):
        ...

result = function(1, 2)
result = function(1, 2)  # Cached result

a = A()
result = a.method(3, 4)
result = a.method(3, 4)  # Cached result

Callback

>>> @bucket
... def function(a, b):
...     return a + b

>>> @function.callback
... def function(callinfo):
...     print(callinfo)

>>> function(1, 2)
3
>>> function(1, 2)
CachedCallInfo(varargs=(), callargs={'a': 1, 'b': 2}, return_value=3, expiration_date=datetime.datetime(2015, 1, 1, 9, 0, 0))
3

Properties

class A(object):
    @property
    @bucket(method=True)
    def this(self):
        ...

    @bucket(method=True)
    @property
    def that(self):
        ...

To use callback with properties, define the method first.

class A(object):
    @bucket(method=True)
    def this(self):
        ...

    @this.callback
    def this(self, callinfo):
        ...

    this = property(this)

Skip cache

@bucket(nocache='refresh')
def function(self, refresh=False):
    ...

function()

# Next call to function would use cached value, unless refresh==True
function(refresh=True)

Ignored parameters

@bucket(ignore=['c'])
def function(a, b, c):
    ...

function(1, 2, 3)
function(1, 2, 4)  # Uses cached result even though c is different

Deferred Writes

To prevent writing to file immediately, a DeferredWriteBucket can be used. Keys are only written to file when bucket.sync() is called.

bucketcache.deferred_write() is a context manager that defers writing until completion of the block.

from bucketcache import deferred_write

bucket = Bucket('path')

with deferred_write(bucket) as deferred:
    deferred[some_key] = some_value
    ...

It’s also possible to create a DeferredWriteBucket manually:

from bucketcache import DeferredWriteBucket

bucket = DeferredWriteBucket('path')

bucket[some_key] = some_value
...

bucket.sync()  # Writing happens here.

Note that calling unload_key() on a DeferredWriteBucket forces a sync.

Logging

You can enable logging as follows:

from bucketcache import logger
logger.disabled = False

Here, logger is an instance of logbook.Logger. By default, the level is set to logbook.NOTSET (i.e. everything is logged).

There is a logger_config object, which currently only has one option:

from bucketcache import logger_config

logger_config.log_full_keys = True

log_full_keys prevents keys from being abbreviated in the log. This may be useful for debugging:

from bucketcache import Bucket, logger, logger_config

logger.disabled = False
logger_config.log_full_keys = True

bucket = Bucket('cache')

class A(object):
    def __init__(self, a, b):
        self.a = a
        self.b = b

    @bucket(method=True)
    def spam(self, eggs):
        return eggs

a = A('this', 'that')
a.spam(5)
DEBUG: bucketcache.log: _hash_for_key <({'a': 'this', 'b': 'that'}, ('spam', OrderedDict([('args', ['self', 'eggs']), ('varargs', None), ('varkw', None), ('defaults', None), ('kwonlyargs', []), ('kwonlydefaults', None), ('annotations', {})])), (), {'eggs': 5})>
DEBUG: bucketcache.log: Done
INFO: bucketcache.log: Attempt load from file: cache/34f8a5019b61dfa8162f09339b72fde9.pickle
INFO: bucketcache.log: Function call loaded from cache: <function spam at 0x10622a848>

Note

At level logbook.DEBUG, BucketCache logs tracebacks for handled exceptions and they will be labeled as such. These are extremely helpful to aid development of backends.