Commit 4e9f8007 authored by Curtis Maloney's avatar Curtis Maloney Committed by Tim Graham
Browse files

Fixed #21125 -- Removed support for cache URI syntax

parent 7fec5a22
Loading
Loading
Loading
Loading
+20 −74
Original line number Diff line number Diff line
@@ -8,9 +8,9 @@ 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 backend URI
(e.g. "memcached://127.0.0.1:11211/") and returns an instance of a backend
cache class.
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.

See docs/topics/cache.txt for information on the public API.
"""
@@ -29,78 +29,17 @@ __all__ = [
    'get_cache', 'cache', 'DEFAULT_CACHE_ALIAS'
]

# Name for use in settings file --> name of module in "backends" directory.
# Any backend scheme that is not in this dictionary is treated as a Python
# import path to a custom backend.
BACKENDS = {
    'memcached': 'memcached',
    'locmem': 'locmem',
    'file': 'filebased',
    'db': 'db',
    'dummy': 'dummy',
}

DEFAULT_CACHE_ALIAS = 'default'

def parse_backend_uri(backend_uri):
    """
    Converts the "backend_uri" into a cache scheme ('db', 'memcached', etc), a
    host and any extra params that are required for the backend. Returns a
    (scheme, host, params) tuple.
    """
    if backend_uri.find(':') == -1:
        raise InvalidCacheBackendError("Backend URI must start with scheme://")
    scheme, rest = backend_uri.split(':', 1)
    if not rest.startswith('//'):
        raise InvalidCacheBackendError("Backend URI must start with scheme://")

    host = rest[2:]
    qpos = rest.find('?')
    if qpos != -1:
        params = dict(parse_qsl(rest[qpos+1:]))
        host = rest[2:qpos]
    else:
        params = {}
    if host.endswith('/'):
        host = host[:-1]

    return scheme, host, params

if DEFAULT_CACHE_ALIAS not in settings.CACHES:
    raise ImproperlyConfigured("You must define a '%s' cache" % DEFAULT_CACHE_ALIAS)

def parse_backend_conf(backend, **kwargs):
    """
    Helper function to parse the backend configuration
    that doesn't use the URI notation.
    """
    # Try to get the CACHES entry for the given backend name first
    conf = settings.CACHES.get(backend, None)
    if conf is not None:
        args = conf.copy()
        args.update(kwargs)
        backend = args.pop('BACKEND')
        location = args.pop('LOCATION', '')
        return backend, location, args
    else:
        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', '')
        return backend, location, kwargs

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

    To load a backend with the old URI-based notation::

        cache = get_cache('locmem://')

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

        cache = get_cache('default')
@@ -114,16 +53,23 @@ def get_cache(backend, **kwargs):

    """
    try:
        if '://' in backend:
            # for backwards compatibility
            backend, location, params = parse_backend_uri(backend)
            if backend in BACKENDS:
                backend = 'django.core.cache.backends.%s' % BACKENDS[backend]
            params.update(kwargs)
            mod = importlib.import_module(backend)
            backend_cls = mod.CacheClass
        # 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:
            backend, location, params = parse_backend_conf(backend, **kwargs)
            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(
+4 −0
Original line number Diff line number Diff line
@@ -414,6 +414,10 @@ Miscellaneous
  Rationale behind this is removal of dependency of non-contrib code on
  contrib applications.

* The old cache URI syntax (e.g. ``"locmem://"``) is no longer supported. It
  still worked, even though it was not documented or officially supported. If
  you're still using it, please update to the current :setting:`CACHES` syntax.

Features deprecated in 1.7
==========================

+1 −17
Original line number Diff line number Diff line
@@ -857,10 +857,6 @@ class DBCacheTests(BaseCacheTests, TransactionTestCase):
        self.cache = get_cache(self.backend_name, LOCATION=self._table_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
        self.perform_cull_test(50, 18)

    def test_old_initialization(self):
        self.cache = get_cache('db://%s?max_entries=30&cull_frequency=0' % self._table_name)
        self.perform_cull_test(50, 18)

    def test_second_call_doesnt_crash(self):
        with six.assertRaisesRegex(self, management.CommandError,
                "Cache table 'test cache table' could not be created"):
@@ -956,10 +952,6 @@ class LocMemCacheTests(unittest.TestCase, BaseCacheTests):
        self.cache = get_cache(self.backend_name, OPTIONS={'MAX_ENTRIES': 30, 'CULL_FREQUENCY': 0})
        self.perform_cull_test(50, 19)

    def test_old_initialization(self):
        self.cache = get_cache('locmem://?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)
@@ -1075,10 +1067,6 @@ class FileBasedCacheTests(unittest.TestCase, BaseCacheTests):
    def test_cull(self):
        self.perform_cull_test(50, 29)

    def test_old_initialization(self):
        self.cache = get_cache('file://%s?max_entries=30' % self.dirname)
        self.perform_cull_test(50, 29)


class CustomCacheKeyValidationTests(unittest.TestCase):
    """
@@ -1088,7 +1076,7 @@ class CustomCacheKeyValidationTests(unittest.TestCase):

    """
    def test_custom_key_validation(self):
        cache = get_cache('cache.liberal_backend://')
        cache = get_cache('cache.liberal_backend.CacheClass')

        # this key is both longer than 250 characters, and has spaces
        key = 'some key with spaces' * 15
@@ -1100,10 +1088,6 @@ class CustomCacheKeyValidationTests(unittest.TestCase):
class GetCacheTests(unittest.TestCase):

    def test_simple(self):
        cache = get_cache('locmem://')
        from django.core.cache.backends.locmem import LocMemCache
        self.assertIsInstance(cache, LocMemCache)

        from django.core.cache import cache
        self.assertIsInstance(cache, get_cache('default').__class__)