Skip to content

Implement support for Flask-Caching #154

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 2 additions & 38 deletions diskcache/djangocache.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,10 @@
DEFAULT_TIMEOUT = 300

from .core import ENOVAL, args_to_key, full_name
from .fanout import FanoutCache
from .fanout import FanoutCache, FanoutMixin


class DjangoCache(BaseCache):
class DjangoCache(BaseCache, FanoutMixin):
"Django-compatible disk and file backed cache."
def __init__(self, directory, params):
"""Initialize DjangoCache instance.
Expand All @@ -29,42 +29,6 @@ def __init__(self, directory, params):
self._cache = FanoutCache(directory, shards, timeout, **options)


@property
def directory(self):
"""Cache directory."""
return self._cache.directory


def cache(self, name):
"""Return Cache with given `name` in subdirectory.

:param str name: subdirectory name for Cache
:return: Cache with given name

"""
return self._cache.cache(name)


def deque(self, name):
"""Return Deque with given `name` in subdirectory.

:param str name: subdirectory name for Deque
:return: Deque with given name

"""
return self._cache.deque(name)


def index(self, name):
"""Return Index with given `name` in subdirectory.

:param str name: subdirectory name for Index
:return: Index with given name

"""
return self._cache.index(name)


def add(self, key, value, timeout=DEFAULT_TIMEOUT, version=None,
read=False, tag=None, retry=True):
"""Set a value in the cache if the key does not already exist. If
Expand Down
37 changes: 37 additions & 0 deletions diskcache/fanout.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,3 +651,40 @@ def index(self, name):


FanoutCache.memoize = Cache.memoize


class FanoutMixin:
@property
def directory(self):
"""Cache directory."""
return self._cache.directory


def cache(self, name):
"""Return Cache with given `name` in subdirectory.

:param str name: subdirectory name for Cache
:return: Cache with given name

"""
return self._cache.cache(name)


def deque(self, name):
"""Return Deque with given `name` in subdirectory.

:param str name: subdirectory name for Deque
:return: Deque with given name

"""
return self._cache.deque(name)


def index(self, name):
"""Return Index with given `name` in subdirectory.

:param str name: subdirectory name for Index
:return: Index with given name

"""
return self._cache.index(name)
180 changes: 180 additions & 0 deletions diskcache/flaskcache.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"Flask-Caching compatible disk and file backed cache."

from flask_caching.backends.cache import BaseCache

from .fanout import FanoutCache, FanoutMixin


class FlaskCache(BaseCache, FanoutMixin):
def __init__(self, directory, params=None, default_timeout=300):
"""Initialize FlaskCache instance.

:param str directory: cache directory
:param dict params: cache parameters

"""
super().__init__(default_timeout=default_timeout)
params = {} if params is None else params
shards = params.get('SHARDS', 8)
timeout = params.get('DATABASE_TIMEOUT', 0.010)
options = params.get('OPTIONS', {})
self._cache = FanoutCache(directory, shards, timeout, **options)


# TODO: Complete the methods below and add tests.


def get(self, key):
"""Look up key in the cache and return the value for it.

:param key: the key to be looked up.
:returns: The value if it exists and is readable, else ``None``.
"""
return None

def delete(self, key):
"""Delete `key` from the cache.

:param key: the key to delete.
:returns: Whether the key existed and has been deleted.
:rtype: boolean
"""
return True

def get_many(self, *keys):
"""Returns a list of values for the given keys.
For each key an item in the list is created::

foo, bar = cache.get_many("foo", "bar")

Has the same error handling as :meth:`get`.

:param keys: The function accepts multiple keys as positional
arguments.
"""
return [self.get(k) for k in keys]

def get_dict(self, *keys):
"""Like :meth:`get_many` but return a dict::

d = cache.get_dict("foo", "bar")
foo = d["foo"]
bar = d["bar"]

:param keys: The function accepts multiple keys as positional
arguments.
"""
return dict(zip(keys, self.get_many(*keys)))

def set(self, key, value, timeout=None):
"""Add a new key/value to the cache (overwrites value, if key already
exists in the cache).

:param key: the key to set
:param value: the value for the key
:param timeout: the cache timeout for the key in seconds (if not
specified, it uses the default timeout). A timeout of
0 indicates that the cache never expires.
:returns: ``True`` if key has been updated, ``False`` for backend
errors. Pickling errors, however, will raise a subclass of
``pickle.PickleError``.
:rtype: boolean
"""
return True

def add(self, key, value, timeout=None):
"""Works like :meth:`set` but does not overwrite the values of already
existing keys.

:param key: the key to set
:param value: the value for the key
:param timeout: the cache timeout for the key in seconds (if not
specified, it uses the default timeout). A timeout of
0 idicates that the cache never expires.
:returns: Same as :meth:`set`, but also ``False`` for already
existing keys.
:rtype: boolean
"""
return True

def set_many(self, mapping, timeout=None):
"""Sets multiple keys and values from a mapping.

:param mapping: a mapping with the keys/values to set.
:param timeout: the cache timeout for the key in seconds (if not
specified, it uses the default timeout). A timeout of
0 idicates that the cache never expires.
:returns: Whether all given keys have been set.
:rtype: boolean
"""
rv = True
for key, value in iteritems_wrapper(mapping):
if not self.set(key, value, timeout):
rv = False
return rv

def delete_many(self, *keys):
"""Deletes multiple keys at once.

:param keys: The function accepts multiple keys as positional
arguments.
:returns: Whether all given keys have been deleted.
:rtype: boolean
"""
if self.ignore_errors:
return all([self.delete(key) for key in keys])
return all(self.delete(key) for key in keys)

def has(self, key):
"""Checks if a key exists in the cache without returning it. This is a
cheap operation that bypasses loading the actual data on the backend.

This method is optional and may not be implemented on all caches.

:param key: the key to check
"""
raise NotImplementedError(
"%s doesn't have an efficient implementation of `has`. That "
"means it is impossible to check whether a key exists without "
"fully loading the key's data. Consider using `self.get` "
"explicitly if you don't care about performance."
)

def clear(self):
"""Clears the cache. Keep in mind that not all caches support
completely clearing the cache.

:returns: Whether the cache has been cleared.
:rtype: boolean
"""
return True

def inc(self, key, delta=1):
"""Increments the value of a key by `delta`. If the key does
not yet exist it is initialized with `delta`.

For supporting caches this is an atomic operation.

:param key: the key to increment.
:param delta: the delta to add.
:returns: The new value or ``None`` for backend errors.
"""
value = (self.get(key) or 0) + delta
return value if self.set(key, value) else None

def dec(self, key, delta=1):
"""Decrements the value of a key by `delta`. If the key does
not yet exist it is initialized with `-delta`.

For supporting caches this is an atomic operation.

:param key: the key to increment.
:param delta: the delta to subtract.
:returns: The new value or `None` for backend errors.
"""
value = (self.get(key) or 0) - delta
return value if self.set(key, value) else None


def make_cache(app, config, args, kwargs):
return FlaskCache(*args, **kwargs)