Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Fixed #21012 -- Thread-local caches, like databases.

  • Loading branch information...
commit ee7eb0f73e6d4699edcf5d357dce715224525cf6 1 parent 3ca0815
Curtis Maloney funkybob authored aaugustin committed
4 django/contrib/sessions/backends/cache.py
View
@@ -1,6 +1,6 @@
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
4 django/contrib/sessions/backends/cached_db.py
View
@@ -6,7 +6,7 @@
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
11 django/contrib/sessions/tests.py
View
@@ -15,7 +15,7 @@
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
@@ -140,7 +140,7 @@ def test_clear(self):
self.assertTrue(self.session.modified)
def test_save(self):
- if (hasattr(self.session, '_cache') and'DummyCache' in
+ if (hasattr(self.session, '_cache') and 'DummyCache' in
settings.CACHES[settings.SESSION_CACHE_ALIAS]['BACKEND']):
raise unittest.SkipTest("Session saving tests require a real cache backend")
self.session.save()
@@ -481,7 +481,7 @@ def test_load_overlong_key(self):
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 @@ def test_default_cache(self):
},
'sessions': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
+ 'LOCATION': 'session',
},
}, SESSION_CACHE_ALIAS='sessions')
def test_non_default_cache(self):
@@ -496,8 +497,8 @@ def test_non_default_cache(self):
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):
4 django/contrib/staticfiles/storage.py
View
@@ -7,7 +7,7 @@
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
133 django/core/cache/__init__.py
View
@@ -14,6 +14,9 @@
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 (
@@ -23,8 +26,8 @@
__all__ = [
- 'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS', 'InvalidCacheBackendError',
- 'CacheKeyWarning', 'BaseCache',
+ 'create_cache', 'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS',
+ 'InvalidCacheBackendError', 'CacheKeyWarning', 'BaseCache',
]
DEFAULT_CACHE_ALIAS = 'default'
@@ -35,43 +38,61 @@
def get_cache(backend, **kwargs):
"""
- Function to load a cache backend dynamically. This is flexible by design
- to allow different use cases:
+ Function to retrieve a configure cache, or create a new one.
+
+ This wrapper is for backward compatibility.
+
+ Use either create_cache or caches directly.
+
+ """
+ warnings.warn("'get_cache' is deprecated. Use either caches or create_cache.",
+ PendingDeprecationWarning, stacklevel=2)
+
+ # If it's just an alias with no options, use the new API
+ if backend in settings.CACHES and not kwargs:
+ return caches[backend]
+
+ return create_cache(backend, **kwargs)
- To load a backend that is pre-defined in the settings::
- cache = get_cache('default')
+def create_cache(backend, **params):
+ """
+ Function to create a cache backend dynamically. This is flexible by design
+ to allow different use cases:
- To load a backend with its dotted import path,
- including arbitrary options::
+ To load a backend with its dotted import path, including options::
- cache = get_cache('django.core.cache.backends.memcached.MemcachedCache', **{
- 'LOCATION': '127.0.0.1:11211', 'TIMEOUT': 30,
+ cache = get_cache('django.core.cache.backends.memcached.MemcachedCache',
+ LOCATION='127.0.0.1:11211', TIMEOUT=30,
})
+ To create a new instance of a cache in settings.CACHES, pass the alias::
+
+ cache = create_cache('default')
+
+ You can also pass extra parameters to override those in settings.CACHES::
+
+ cache = create_cache('default', LOCATION='bar')
+
"""
+
+ # We can name a cache from settings.CACHES and update its params
+ try:
+ conf = settings.CACHES[backend]
+ except KeyError:
+ pass
+ else:
+ params = conf.copy()
+ params.update(params)
+ backend = params.pop('BACKEND')
+
try:
- # Try to get the CACHES entry for the given backend name first
- try:
- conf = settings.CACHES[backend]
- except KeyError:
- try:
- # Trying to import the given backend, in case it's a dotted path
- import_by_path(backend)
- except ImproperlyConfigured as e:
- raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
- backend, e))
- location = kwargs.pop('LOCATION', '')
- params = kwargs
- else:
- params = conf.copy()
- params.update(kwargs)
- backend = params.pop('BACKEND')
- location = params.pop('LOCATION', '')
backend_cls = import_by_path(backend)
- except (AttributeError, ImportError, ImproperlyConfigured) as e:
- raise InvalidCacheBackendError(
- "Could not find backend '%s': %s" % (backend, e))
+ except ImproperlyConfigured as e:
+ raise InvalidCacheBackendError("Could not find backend '%s': %s" % (
+ backend, e
+ ))
+ location = params.pop('LOCATION', '')
cache = backend_cls(location, params)
# Some caches -- python-memcached in particular -- need to do a cleanup at the
# end of a request cycle. If not implemented in a particular backend
@@ -79,4 +100,54 @@ 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 __eq__(self, other):
+ return caches[DEFAULT_CACHE_ALIAS] == other
+
+ def __ne__(self, other):
+ return caches[DEFAULT_CACHE_ALIAS] != other
+
+cache = DefaultCacheProxy()
14 django/core/cache/backends/memcached.py
View
@@ -2,13 +2,13 @@
import time
import pickle
-from threading import local
from django.core.cache.backends.base import BaseCache, DEFAULT_TIMEOUT
from django.utils import six
from django.utils.deprecation import RenameMethodsBase
from django.utils.encoding import force_str
+from django.utils.functional import cached_property
class BaseMemcachedCacheMethods(RenameMethodsBase):
@@ -177,24 +177,14 @@ class PyLibMCCache(BaseMemcachedCache):
"An implementation of a cache binding using pylibmc"
def __init__(self, server, params):
import pylibmc
- self._local = local()
super(PyLibMCCache, self).__init__(server, params,
library=pylibmc,
value_not_found_exception=pylibmc.NotFound)
- @property
+ @cached_property
def _cache(self):
- # PylibMC uses cache options as the 'behaviors' attribute.
- # It also needs to use threadlocals, because some versions of
- # PylibMC don't play well with the GIL.
- client = getattr(self._local, 'client', None)
- if client:
- return client
-
client = self._lib.Client(self._servers)
if self._options:
client.behaviors = self._options
- self._local.client = client
-
return client
4 django/core/management/commands/createcachetable.py
View
@@ -1,7 +1,7 @@
from optparse import make_option
from django.conf import settings
-from django.core.cache import get_cache
+from django.core.cache import create_cache
from django.core.cache.backends.db import BaseDatabaseCache
from django.core.management.base import BaseCommand, CommandError
from django.db import connections, router, transaction, models, DEFAULT_DB_ALIAS
@@ -30,7 +30,7 @@ def handle(self, *tablenames, **options):
self.create_table(db, tablename)
else:
for cache_alias in settings.CACHES:
- cache = get_cache(cache_alias)
+ cache = create_cache(cache_alias)
if isinstance(cache, BaseDatabaseCache):
self.create_table(db, cache._table)
48 django/middleware/cache.py
View
@@ -46,7 +46,7 @@
import warnings
from django.conf import settings
-from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS
+from django.core.cache import create_cache, caches, DEFAULT_CACHE_ALIAS
from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age
@@ -64,7 +64,7 @@ def __init__(self):
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
- self.cache = get_cache(self.cache_alias)
+ self.cache = caches[self.cache_alias]
def _session_accessed(self, request):
try:
@@ -122,10 +122,9 @@ class FetchFromCacheMiddleware(object):
MIDDLEWARE_CLASSES so that it'll get called last during the request phase.
"""
def __init__(self):
- self.cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
- self.cache = get_cache(self.cache_alias)
+ self.cache = caches[self.cache_alias]
def process_request(self, request):
"""
@@ -169,39 +168,32 @@ def __init__(self, cache_timeout=None, cache_anonymous_only=None, **kwargs):
# we fall back to system defaults. If it is not provided at all,
# we need to use middleware defaults.
- cache_kwargs = {}
-
try:
- self.key_prefix = kwargs['key_prefix']
- if self.key_prefix is not None:
- cache_kwargs['KEY_PREFIX'] = self.key_prefix
- else:
- self.key_prefix = ''
+ key_prefix = kwargs['key_prefix']
+ if key_prefix is None:
+ key_prefix = ''
except KeyError:
- self.key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
- cache_kwargs['KEY_PREFIX'] = self.key_prefix
+ key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
+ self.key_prefix = key_prefix
try:
- self.cache_alias = kwargs['cache_alias']
- if self.cache_alias is None:
- self.cache_alias = DEFAULT_CACHE_ALIAS
- if cache_timeout is not None:
- cache_kwargs['TIMEOUT'] = cache_timeout
+ cache_alias = kwargs['cache_alias']
+ if cache_alias is None:
+ cache_alias = DEFAULT_CACHE_ALIAS
except KeyError:
- self.cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
- if cache_timeout is None:
- cache_kwargs['TIMEOUT'] = settings.CACHE_MIDDLEWARE_SECONDS
- else:
- cache_kwargs['TIMEOUT'] = cache_timeout
+ cache_alias = settings.CACHE_MIDDLEWARE_ALIAS
+ self.cache_alias = cache_alias
+
+ if cache_timeout is None:
+ cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
+ self.cache_timeout = cache_timeout
if cache_anonymous_only is None:
- self.cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
- else:
- self.cache_anonymous_only = cache_anonymous_only
+ cache_anonymous_only = getattr(settings, 'CACHE_MIDDLEWARE_ANONYMOUS_ONLY', False)
+ self.cache_anonymous_only = cache_anonymous_only
if self.cache_anonymous_only:
msg = "CACHE_MIDDLEWARE_ANONYMOUS_ONLY has been deprecated and will be removed in Django 1.8."
warnings.warn(msg, DeprecationWarning, stacklevel=1)
- self.cache = get_cache(self.cache_alias, **cache_kwargs)
- self.cache_timeout = self.cache.default_timeout
+ self.cache = create_cache(self.cache_alias)
6 django/templatetags/cache.py
View
@@ -1,13 +1,13 @@
from __future__ import unicode_literals
-from django.core.cache import get_cache, InvalidCacheBackendError
+from django.core.cache import caches, InvalidCacheBackendError
from django.core.cache.utils import make_template_fragment_key
from django.template import Library, Node, TemplateSyntaxError, VariableDoesNotExist
register = Library()
try:
- default_cache = get_cache('template_fragments')
+ default_cache = caches['template_fragments']
except InvalidCacheBackendError:
from django.core.cache import cache as default_cache
@@ -35,7 +35,7 @@ def render(self, context):
except VariableDoesNotExist:
raise TemplateSyntaxError('"cache" tag got an unknown variable: %r' % self.cache_name.var)
try:
- cache = get_cache(cache_name)
+ cache = caches[cache_name]
except InvalidCacheBackendError:
raise TemplateSyntaxError('Invalid cache name specified for cache tag: %r' % cache_name)
else:
6 django/utils/cache.py
View
@@ -23,7 +23,7 @@
import time
from django.conf import settings
-from django.core.cache import get_cache
+from django.core.cache import caches
from django.utils.encoding import iri_to_uri, force_bytes, force_text
from django.utils.http import http_date
from django.utils.timezone import get_current_timezone_name
@@ -219,7 +219,7 @@ def get_cache_key(request, key_prefix=None, method='GET', cache=None):
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
cache_key = _generate_cache_header_key(key_prefix, request)
if cache is None:
- cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
+ cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
headerlist = cache.get(cache_key, None)
if headerlist is not None:
return _generate_cache_key(request, method, headerlist, key_prefix)
@@ -246,7 +246,7 @@ def learn_cache_key(request, response, cache_timeout=None, key_prefix=None, cach
cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
cache_key = _generate_cache_header_key(key_prefix, request)
if cache is None:
- cache = get_cache(settings.CACHE_MIDDLEWARE_ALIAS)
+ cache = caches[settings.CACHE_MIDDLEWARE_ALIAS]
if response.has_header('Vary'):
is_accept_language_redundant = settings.USE_I18N or settings.USE_L10N
# If i18n or l10n are used, the generated cache key will be suffixed
3  docs/internals/deprecation.txt
View
@@ -215,6 +215,9 @@ these changes.
* The internal ``django.utils.functional.memoize`` will be removed.
+* ``get_cache`` from django.core.cache will be removed. Instead, use
+ ``create_cache`` or ``caches``, depending on your need.
+
2.0
---
26 docs/releases/1.7.txt
View
@@ -269,6 +269,26 @@ Minor features
allowing the ``published`` element to be included in the feed (which
relies on ``pubdate``).
+Cache
+^^^^^
+
+* Access to caches configured in ``settings.CACHES`` is now available via
+ ``django.core.cache.caches``. This will now return a different instance per
+ thread.
+
+* A new function ``django.core.cache.create_cache`` has been added to make it
+ clearer what's happening. ``django.core.cache.get_cache`` will call this
+ if it's passed anything other than just a cache config alias.
+
+* ``django.core.cache.get_cache`` has been deprecated. Use
+ ``django.core.cache.caches`` to access caches configurd in
+ ``settings.CACHES``, or ``django.core.cache.create_cache`` to create ad-hoc
+ instances.
+
+* All thread safety in cache backends has been removed, as
+ ``django.core.cache.caches`` now yields differend backend instances per
+ thread.
+
Email
^^^^^
@@ -643,6 +663,12 @@ Miscellaneous
Features deprecated in 1.7
==========================
+``django.core.cache.get_cache``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+``django.core.cache.get_cache`` has been supplanted by
+``django.core.cache.caches`` and ``django.core.cache.create_cache``.
+
``django.utils.dictconfig``/``django.utils.importlib``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57 docs/topics/cache.txt
View
@@ -703,7 +703,22 @@ pickling.)
Accessing the cache
-------------------
-.. function:: django.core.cache.get_cache(backend, **kwargs)
+.. versionadded:: 1.7
+
+You can access the caches configured in ``settings.CACHES`` through the
+dict-like ``django.core.cache.caches`` object. Repeated requests for the same
+alias will return the same object.
+
+ >>> from django.core.cache import caches
+ >>> cache1 = caches['myalias']
+ >>> cache2 = caches['myalias']
+ >>> cache1 is cache2
+ True
+
+If the named key does not exist, ``InvalidCacheBackendError`` will be raised.
+
+The ``caches`` dict is thread aware, so a different instance of each alias will
+be returned for each thread.
The cache module, ``django.core.cache``, has a ``cache`` object that's
automatically created from the ``'default'`` entry in the :setting:`CACHES`
@@ -711,13 +726,43 @@ setting::
>>> from django.core.cache import cache
-If you have multiple caches defined in :setting:`CACHES`, then you can use
-:func:`django.core.cache.get_cache` to retrieve a cache object for any key::
+This is a proxy object to caches['default']. It is provided for backward
+compatiblity.
- >>> from django.core.cache import get_cache
- >>> cache = get_cache('alternate')
+.. function:: django.core.cache.create_cache(backend, **kwargs)
-If the named key does not exist, ``InvalidCacheBackendError`` will be raised.
+You can create caches from ad-hoc configurations using ``create_cache``.
+
+ >>> from django.core.cache import create_cache
+ # Create an instance of a specific backend
+ >>> cache = create_cache(
+ 'django.core.cache.backends.memcached.MemcachedCache',
+ LOCATION='/tmp/memcached.sock'
+ )
+ # Create a separate copy of the 'default' cache:
+ >>> new_default = create_cache('default')
+ # Create a cache with the same config as 'default', but a different timeout
+ >>> cache2 = create_cache('default', TIMEOUT=1)
+
+This is guaranteed to always create a new instance.
+
+.. function:: django.core.cache.get_cache(backend, **kwargs)
+
+.. deprecated:: 1.7
+ This function has been deprecated in favour of ``caches`` and
+ ``create_cache``.
+
+Before Django 1.7 this was the only way to get a cache instance. Now it acts
+as a wrapper to ``create_cache``, except in the case where it is passed only a
+configured alias, where it will return the cache from ``caches``::
+
+ >>> from django.core.cache import get_cache
+ # Passes call to create_cache
+ >>> cache = get_cache('django.core.cache.backends.memcached.MemcachedCache', LOCATION='127.0.0.2')
+ # Creates a new cache based on the config in settings.CACHES['default']
+ >>> cache = get_cache('default', TIMEOUT=300)
+ # Returns instance from caches object
+ >>> cache = get_cache('default')
Basic usage
118 tests/cache/tests.py
View
@@ -10,13 +10,14 @@
import re
import string
import tempfile
+import threading
import time
import unittest
import warnings
from django.conf import settings
from django.core import management
-from django.core.cache import get_cache
+from django.core.cache import create_cache, caches
from django.core.cache.backends.base import (CacheKeyWarning,
InvalidCacheBackendError)
from django.db import connection, router, transaction
@@ -55,7 +56,7 @@ class DummyCacheTests(unittest.TestCase):
backend_name = 'django.core.cache.backends.dummy.DummyCache'
def setUp(self):
- self.cache = get_cache(self.backend_name)
+ self.cache = create_cache(self.backend_name)
def test_simple(self):
"Dummy cache backend ignores cache set calls"
@@ -840,11 +841,11 @@ def setUp(self):
# Spaces are used in the table name to ensure quoting/escaping is working
self._table_name = 'test cache table'
management.call_command('createcachetable', verbosity=0, interactive=False)
- self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30})
- self.prefix_cache = get_cache(self.backend_name, LOCATION=self._table_name, KEY_PREFIX='cacheprefix')
- self.v2_cache = get_cache(self.backend_name, LOCATION=self._table_name, VERSION=2)
- self.custom_key_cache = get_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION=custom_key_func)
- self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION='cache.tests.custom_key_func')
+ self.cache = create_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30})
+ self.prefix_cache = create_cache(self.backend_name, LOCATION=self._table_name, KEY_PREFIX='cacheprefix')
+ self.v2_cache = create_cache(self.backend_name, LOCATION=self._table_name, VERSION=2)
+ self.custom_key_cache = create_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION=custom_key_func)
+ self.custom_key_cache2 = create_cache(self.backend_name, LOCATION=self._table_name, KEY_FUNCTION='cache.tests.custom_key_func')
def tearDown(self):
cursor = connection.cursor()
@@ -855,7 +856,7 @@ def test_cull(self):
self.perform_cull_test(50, 29)
def test_zero_cull(self):
- self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
+ self.cache = create_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
self.perform_cull_test(50, 18)
def test_second_call_doesnt_crash(self):
@@ -950,11 +951,11 @@ class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
def setUp(self):
self.factory = RequestFactory()
- self.cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30})
- self.prefix_cache = get_cache(self.backend_name, KEY_PREFIX='cacheprefix')
- self.v2_cache = get_cache(self.backend_name, VERSION=2)
- self.custom_key_cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION=custom_key_func)
- self.custom_key_cache2 = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION='cache.tests.custom_key_func')
+ self.cache = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30})
+ self.prefix_cache = create_cache(self.backend_name, KEY_PREFIX='cacheprefix')
+ self.v2_cache = create_cache(self.backend_name, VERSION=2)
+ self.custom_key_cache = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION=custom_key_func)
+ self.custom_key_cache2 = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30}, KEY_FUNCTION='cache.tests.custom_key_func')
# LocMem requires a hack to make the other caches
# share a data store with the 'normal' cache.
@@ -977,13 +978,13 @@ def test_cull(self):
self.perform_cull_test(50, 29)
def test_zero_cull(self):
- self.cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
+ self.cache = create_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
self.perform_cull_test(50, 19)
def test_multiple_caches(self):
"Check that multiple locmem caches are isolated"
- mirror_cache = get_cache(self.backend_name)
- other_cache = get_cache(self.backend_name, LOCATION='other')
+ mirror_cache = create_cache(self.backend_name)
+ other_cache = create_cache(self.backend_name, LOCATION='other')
self.cache.set('value1', 42)
self.assertEqual(mirror_cache.get('value1'), 42)
@@ -1017,11 +1018,11 @@ def setUp(self):
if cache['BACKEND'].startswith('django.core.cache.backends.memcached.'):
break
random_prefix = ''.join(random.choice(string.ascii_letters) for x in range(10))
- self.cache = get_cache(cache_key)
- self.prefix_cache = get_cache(cache_key, KEY_PREFIX=random_prefix)
- self.v2_cache = get_cache(cache_key, VERSION=2)
- self.custom_key_cache = get_cache(cache_key, KEY_FUNCTION=custom_key_func)
- self.custom_key_cache2 = get_cache(cache_key, KEY_FUNCTION='cache.tests.custom_key_func')
+ self.cache = caches[cache_key]
+ self.prefix_cache = create_cache(cache_key, KEY_PREFIX=random_prefix)
+ self.v2_cache = create_cache(cache_key, VERSION=2)
+ self.custom_key_cache = create_cache(cache_key, KEY_FUNCTION=custom_key_func)
+ self.custom_key_cache2 = create_cache(cache_key, KEY_FUNCTION='cache.tests.custom_key_func')
def tearDown(self):
self.cache.clear()
@@ -1050,7 +1051,7 @@ def test_memcached_uses_highest_pickle_version(self):
# Regression test for #19810
for cache_key, cache in settings.CACHES.items():
if cache['BACKEND'] == 'django.core.cache.backends.memcached.MemcachedCache':
- self.assertEqual(get_cache(cache_key)._cache.pickleProtocol,
+ self.assertEqual(caches[cache_key]._cache.pickleProtocol,
pickle.HIGHEST_PROTOCOL)
@@ -1063,11 +1064,11 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
def setUp(self):
self.factory = RequestFactory()
self.dirname = tempfile.mkdtemp()
- self.cache = get_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30})
- self.prefix_cache = get_cache(self.backend_name, LOCATION=self.dirname, KEY_PREFIX='cacheprefix')
- self.v2_cache = get_cache(self.backend_name, LOCATION=self.dirname, VERSION=2)
- self.custom_key_cache = get_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION=custom_key_func)
- self.custom_key_cache2 = get_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION='cache.tests.custom_key_func')
+ self.cache = create_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30})
+ self.prefix_cache = create_cache(self.backend_name, LOCATION=self.dirname, KEY_PREFIX='cacheprefix')
+ self.v2_cache = create_cache(self.backend_name, LOCATION=self.dirname, VERSION=2)
+ self.custom_key_cache = create_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION=custom_key_func)
+ self.custom_key_cache2 = create_cache(self.backend_name, LOCATION=self.dirname, KEY_FUNCTION='cache.tests.custom_key_func')
def tearDown(self):
self.cache.clear()
@@ -1097,7 +1098,7 @@ def test_creates_cache_dir_if_nonexistent(self):
def test_zero_cull(self):
# Regression test for #15806
- self.cache = get_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
+ self.cache = create_cache(self.backend_name, LOCATION=self.dirname, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
self.perform_cull_test(50, 19)
@@ -1109,7 +1110,7 @@ class CustomCacheKeyValidationTests(unittest.TestCase):
"""
def test_custom_key_validation(self):
- cache = get_cache('cache.liberal_backend.CacheClass')
+ cache = create_cache('cache.liberal_backend.CacheClass')
# this key is both longer than 250 characters, and has spaces
key = 'some key with spaces' * 15
@@ -1121,18 +1122,23 @@ def test_custom_key_validation(self):
class GetCacheTests(unittest.TestCase):
def test_simple(self):
- from django.core.cache import cache
- self.assertIsInstance(cache, get_cache('default').__class__)
+ from django.core.cache import caches, DEFAULT_CACHE_ALIAS
+ self.assertIsInstance(
+ caches[DEFAULT_CACHE_ALIAS],
+ create_cache('default').__class__
+ )
- cache = get_cache(
- 'django.core.cache.backends.dummy.DummyCache', **{'TIMEOUT': 120})
+ cache = create_cache(
+ 'django.core.cache.backends.dummy.DummyCache',
+ **{'TIMEOUT': 120}
+ )
self.assertEqual(cache.default_timeout, 120)
- self.assertRaises(InvalidCacheBackendError, get_cache, 'does_not_exist')
+ self.assertRaises(InvalidCacheBackendError, create_cache, 'does_not_exist')
def test_close(self):
from django.core import signals
- cache = get_cache('cache.closeable_cache.CacheClass')
+ cache = create_cache('cache.closeable_cache.CacheClass')
self.assertFalse(cache.closed)
signals.request_finished.send(self.__class__)
self.assertTrue(cache.closed)
@@ -1153,7 +1159,7 @@ class CacheUtils(TestCase):
def setUp(self):
self.path = '/cache/test/'
- self.cache = get_cache('default')
+ self.cache = caches['default']
self.factory = RequestFactory()
def tearDown(self):
@@ -1261,7 +1267,7 @@ class CacheHEADTest(TestCase):
def setUp(self):
self.path = '/cache/test/'
- self.cache = get_cache('default')
+ self.cache = caches['default']
self.factory = RequestFactory()
def tearDown(self):
@@ -1314,7 +1320,7 @@ class CacheI18nTest(TestCase):
def setUp(self):
self.path = '/cache/test/'
- self.cache = get_cache('default')
+ self.cache = create_cache('default')
self.factory = RequestFactory()
def tearDown(self):
@@ -1581,8 +1587,8 @@ class CacheMiddlewareTest(IgnoreDeprecationWarningsMixin, TestCase):
def setUp(self):
super(CacheMiddlewareTest, self).setUp()
self.factory = RequestFactory()
- self.default_cache = get_cache('default')
- self.other_cache = get_cache('other')
+ self.default_cache = create_cache('default')
+ self.other_cache = create_cache('other')
def tearDown(self):
self.default_cache.clear()
@@ -1608,7 +1614,7 @@ def test_constructor(self):
# First, test with "defaults":
as_view_decorator = CacheMiddleware(cache_alias=None, key_prefix=None)
- self.assertEqual(as_view_decorator.cache_timeout, 300) # Timeout value for 'default' cache, i.e. 300
+ self.assertEqual(as_view_decorator.cache_timeout, 30) # Timeout value for 'default' cache, i.e. 30
self.assertEqual(as_view_decorator.key_prefix, '')
self.assertEqual(as_view_decorator.cache_alias, 'default') # Value of DEFAULT_CACHE_ALIAS from django.core.cache
self.assertEqual(as_view_decorator.cache_anonymous_only, False)
@@ -1755,7 +1761,7 @@ def test_view_decorator(self):
time.sleep(2)
# ... the default cache will still hit
- get_cache('default')
+ caches['default']
response = default_view(request, '11')
self.assertEqual(response.content, b'Hello World 1')
@@ -1801,7 +1807,7 @@ class TestWithTemplateResponse(TestCase):
"""
def setUp(self):
self.path = '/cache/test/'
- self.cache = get_cache('default')
+ self.cache = create_cache('default')
self.factory = RequestFactory()
def tearDown(self):
@@ -1904,3 +1910,29 @@ def test_proper_escaping(self):
key = make_template_fragment_key('spam', ['abc:def%'])
self.assertEqual(key,
'template.cache.spam.f27688177baec990cdf3fbd9d9c3f469')
+
+class CacheHandlerTest(TestCase):
+ def test_same_instance(self):
+ """
+ Attempting to retrieve the same alias should yield the same instance.
+ """
+ cache1 = caches['default']
+ cache2 = caches['default']
+
+ self.assertTrue(cache1 is cache2)
+
+ def test_per_thread(self):
+ """
+ Requesting the same alias from separate threads should yield separate
+ instances.
+ """
+ c = []
+ def runner():
+ c.append(caches['default'])
+
+ for x in range(2):
+ t = threading.Thread(target=runner)
+ t.start()
+ t.join()
+
+ self.assertFalse(c[0] is c[1])
Please sign in to comment.
Something went wrong with that request. Please try again.