diff --git a/django/core/cache/__init__.py b/django/core/cache/__init__.py index 2cdbe4a04d..721e24921c 100644 --- a/django/core/cache/__init__.py +++ b/django/core/cache/__init__.py @@ -53,7 +53,12 @@ def get_cache(backend, **kwargs): """ warnings.warn("'get_cache' is deprecated in favor of 'caches'.", PendingDeprecationWarning, stacklevel=2) - return _create_cache(backend, **kwargs) + cache = _create_cache(backend, **kwargs) + # 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 + # cache.close is a no-op + signals.request_finished.connect(cache.close) + return cache def _create_cache(backend, **kwargs): @@ -79,12 +84,7 @@ def _create_cache(backend, **kwargs): except (AttributeError, ImportError, ImproperlyConfigured) as e: raise InvalidCacheBackendError( "Could not find backend '%s': %s" % (backend, e)) - 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 - # cache.close is a no-op - signals.request_finished.connect(cache.close) - return cache + return backend_cls(location, params) class CacheHandler(object): @@ -98,8 +98,10 @@ class CacheHandler(object): def __getitem__(self, alias): try: - return getattr(self._caches, alias) + return self._caches.caches[alias] except AttributeError: + self._caches.caches = {} + except KeyError: pass if alias not in settings.CACHES: @@ -108,10 +110,12 @@ class CacheHandler(object): ) cache = _create_cache(alias) - setattr(self._caches, alias, cache) - + self._caches.caches[alias] = cache return cache + def all(self): + return getattr(self._caches, 'caches', {}).values() + caches = CacheHandler() @@ -141,3 +145,11 @@ class DefaultCacheProxy(object): return caches[DEFAULT_CACHE_ALIAS] != other cache = DefaultCacheProxy() + +def close_caches(**kwargs): + # 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 + # cache.close is a no-op + for cache in caches.all(): + cache.close() +signals.request_finished.connect(close_caches) diff --git a/tests/cache/tests.py b/tests/cache/tests.py index c4a941dbf8..5ca9a7d4db 100644 --- a/tests/cache/tests.py +++ b/tests/cache/tests.py @@ -203,7 +203,6 @@ _caches_setting_base = { 'custom_key2': {'KEY_FUNCTION': 'cache.tests.custom_key_func'}, 'cull': {'OPTIONS': {'MAX_ENTRIES': 30}}, 'zero_cull': {'OPTIONS': {'CULL_FREQUENCY': 0, 'MAX_ENTRIES': 30}}, - 'other': {'LOCATION': 'other'}, } @@ -1014,6 +1013,13 @@ class LocMemCacheTests(BaseCacheTests, TestCase): caches['custom_key2']._cache = cache._cache caches['custom_key2']._expire_info = cache._expire_info + @override_settings(CACHES={ + 'default': {'BACKEND': 'django.core.cache.backends.locmem.LocMemCache'}, + 'other': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'LOCATION': 'other' + }, + }) def test_multiple_caches(self): "Check that multiple locmem caches are isolated" cache.set('value', 42) @@ -1139,6 +1145,12 @@ class CustomCacheKeyValidationTests(TestCase): self.assertEqual(cache.get(key), val) +@override_settings( + CACHES={ + 'default': { + 'BACKEND': 'cache.closeable_cache.CacheClass', + } + },) class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase): def test_simple(self): @@ -1157,6 +1169,12 @@ class GetCacheTests(IgnorePendingDeprecationWarningsMixin, TestCase): self.assertRaises(InvalidCacheBackendError, get_cache, 'does_not_exist') def test_close(self): + from django.core import signals + self.assertFalse(cache.closed) + signals.request_finished.send(self.__class__) + self.assertTrue(cache.closed) + + def test_close_deprecated(self): from django.core.cache import get_cache from django.core import signals cache = get_cache('cache.closeable_cache.CacheClass')