Commit ffc37e23 authored by Curtis Maloney's avatar Curtis Maloney Committed by Aymeric Augustin
Browse files

Fixed #21012 -- New API to access cache backends.

Thanks Curtis Malony and Florian Apolloner.

Squashed commit of the following:

commit 3380495e93f5e81b80a251b03ddb0a80b17685f5
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date:   Sat Nov 23 14:18:07 2013 +0100

    Looked up the template_fragments cache at runtime.

commit 905a74f52b24a198f802520ff06290a94dedc687
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date:   Sat Nov 23 14:19:48 2013 +0100

    Removed all uses of create_cache.

    Refactored the cache tests significantly.

    Made it safe to override the CACHES setting.

commit 35e289fe9285feffed3c60657af9279a6a2cfccc
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date:   Sat Nov 23 12:23:57 2013 +0100

    Removed create_cache function.

commit 8e274f747a1f1c0c0e6c37873e29067f7fa022e8
Author: Aymeric Augustin <aymeric.augustin@m4x.org>
Date:   Sat Nov 23 12:04:52 2013 +0100

    Updated docs to describe a simplified cache backend API.

commit ee7eb0f73e6d4699edcf5d357dce715224525cf6
Author: Curtis Maloney <curtis@tinbrain.net>
Date:   Sat Oct 19 09:49:24 2013 +1100

    Fixed #21012 -- Thread-local caches, like databases.
parent 3ca0815c
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
from django.conf import settings
from django.contrib.sessions.backends.base import SessionBase, CreateError
from django.core.cache import get_cache
from django.core.cache import caches
from django.utils.six.moves import xrange

KEY_PREFIX = "django.contrib.sessions.cache"
@@ -11,7 +11,7 @@ class SessionStore(SessionBase):
    A cache-based session store.
    """
    def __init__(self, session_key=None):
        self._cache = get_cache(settings.SESSION_CACHE_ALIAS)
        self._cache = caches[settings.SESSION_CACHE_ALIAS]
        super(SessionStore, self).__init__(session_key)

    @property
+2 −2
Original line number Diff line number Diff line
@@ -6,7 +6,7 @@ import logging

from django.conf import settings
from django.contrib.sessions.backends.db import SessionStore as DBStore
from django.core.cache import get_cache
from django.core.cache import caches
from django.core.exceptions import SuspiciousOperation
from django.utils import timezone
from django.utils.encoding import force_text
@@ -20,7 +20,7 @@ class SessionStore(DBStore):
    """

    def __init__(self, session_key=None):
        self._cache = get_cache(settings.SESSION_CACHE_ALIAS)
        self._cache = caches[settings.SESSION_CACHE_ALIAS]
        super(SessionStore, self).__init__(session_key)

    @property
+6 −5
Original line number Diff line number Diff line
@@ -15,7 +15,7 @@ from django.contrib.sessions.backends.file import SessionStore as FileSession
from django.contrib.sessions.backends.signed_cookies import SessionStore as CookieSession
from django.contrib.sessions.models import Session
from django.contrib.sessions.middleware import SessionMiddleware
from django.core.cache import get_cache
from django.core.cache import caches
from django.core.cache.backends.base import InvalidCacheBackendError
from django.core import management
from django.core.exceptions import ImproperlyConfigured
@@ -481,7 +481,7 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):

    def test_default_cache(self):
        self.session.save()
        self.assertNotEqual(get_cache('default').get(self.session.cache_key), None)
        self.assertNotEqual(caches['default'].get(self.session.cache_key), None)

    @override_settings(CACHES={
        'default': {
@@ -489,6 +489,7 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
        },
        'sessions': {
            'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
            'LOCATION': 'session',
        },
    }, SESSION_CACHE_ALIAS='sessions')
    def test_non_default_cache(self):
@@ -496,8 +497,8 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
        self.session = self.backend()

        self.session.save()
        self.assertEqual(get_cache('default').get(self.session.cache_key), None)
        self.assertNotEqual(get_cache('sessions').get(self.session.cache_key), None)
        self.assertEqual(caches['default'].get(self.session.cache_key), None)
        self.assertNotEqual(caches['sessions'].get(self.session.cache_key), None)


class SessionMiddlewareTests(unittest.TestCase):
+2 −2
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ import posixpath
import re

from django.conf import settings
from django.core.cache import (get_cache, InvalidCacheBackendError,
from django.core.cache import (caches, InvalidCacheBackendError,
                               cache as default_cache)
from django.core.exceptions import ImproperlyConfigured
from django.core.files.base import ContentFile
@@ -56,7 +56,7 @@ class CachedFilesMixin(object):
    def __init__(self, *args, **kwargs):
        super(CachedFilesMixin, self).__init__(*args, **kwargs)
        try:
            self.cache = get_cache('staticfiles')
            self.cache = caches['staticfiles']
        except InvalidCacheBackendError:
            # Use the default backend
            self.cache = default_cache
+68 −8
Original line number Diff line number Diff line
@@ -6,14 +6,15 @@ In a nutshell, a cache is a set of values -- which can be any object that
may be pickled -- identified by string keys.  For the complete API, see
the abstract BaseCache class in django.core.cache.backends.base.

Client code should not access a cache backend directly; instead it should
either use the "cache" variable made available here, or it should use the
get_cache() function made available here. get_cache() takes a CACHES alias or a
backend path and config parameters, and returns an instance of a backend cache
class.
Client code should use the `cache` variable defined here to access the default
cache backend and look up non-default cache backends in the `caches` dict-like
object.

See docs/topics/cache.txt for information on the public API.
"""
from threading import local
import warnings

from django.conf import settings
from django.core import signals
from django.core.cache.backends.base import (
@@ -35,14 +36,14 @@ if DEFAULT_CACHE_ALIAS not in settings.CACHES:

def get_cache(backend, **kwargs):
    """
    Function to load a cache backend dynamically. This is flexible by design
    Function to create a cache backend dynamically. This is flexible by design
    to allow different use cases:

    To load a backend that is pre-defined in the settings::

        cache = get_cache('default')

    To load a backend with its dotted import path,
    To create a backend with its dotted import path,
    including arbitrary options::

        cache = get_cache('django.core.cache.backends.memcached.MemcachedCache', **{
@@ -50,6 +51,12 @@ def get_cache(backend, **kwargs):
        })

    """
    warnings.warn("'get_cache' is deprecated in favor of 'caches'.",
                  PendingDeprecationWarning, stacklevel=2)
    return _create_cache(backend, **kwargs)


def _create_cache(backend, **kwargs):
    try:
        # Try to get the CACHES entry for the given backend name first
        try:
@@ -79,4 +86,57 @@ def get_cache(backend, **kwargs):
    signals.request_finished.connect(cache.close)
    return cache

cache = get_cache(DEFAULT_CACHE_ALIAS)

class CacheHandler(object):
    """
    A Cache Handler to manage access to Cache instances.

    Ensures only one instance of each alias exists per thread.
    """
    def __init__(self):
        self._caches = local()

    def __getitem__(self, alias):
        try:
            return getattr(self._caches, alias)
        except AttributeError:
            pass

        if alias not in settings.CACHES:
            raise InvalidCacheBackendError(
                "Could not find config for '%s' in settings.CACHES" % alias
            )

        cache = _create_cache(alias)
        setattr(self._caches, alias, cache)

        return cache

caches = CacheHandler()

class DefaultCacheProxy(object):
    """
    Proxy access to the default Cache object's attributes.

    This allows the legacy `cache` object to be thread-safe using the new
    ``caches`` API.
    """
    def __getattr__(self, name):
        return getattr(caches[DEFAULT_CACHE_ALIAS], name)

    def __setattr__(self, name, value):
        return setattr(caches[DEFAULT_CACHE_ALIAS], name, value)

    def __delattr__(self, name):
        return delattr(caches[DEFAULT_CACHE_ALIAS], name)

    def __contains__(self, key):
        return key in caches[DEFAULT_CACHE_ALIAS]

    def __eq__(self, other):
        return caches[DEFAULT_CACHE_ALIAS] == other

    def __ne__(self, other):
        return caches[DEFAULT_CACHE_ALIAS] != other

cache = DefaultCacheProxy()
Loading