Skip to content
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

Support lower case options for Django Redis cache backend #550

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
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
17 changes: 13 additions & 4 deletions environ/environ.py
Original file line number Diff line number Diff line change
Expand Up @@ -689,19 +689,28 @@ def cache_url_config(cls, url, backend=None):
else:
config['LOCATION'] = locations

if backend:
config['BACKEND'] = backend

if url.query:
config_options = {}
# Django Redis cache backend expects options in lower case
# while "django_redis" expects them in upper case
backend = config['BACKEND']
if backend == 'django.core.cache.backends.redis.RedisCache':
key_modifier = 'lower'
else:
key_modifier = 'upper'

for k, v in parse_qs(url.query).items():
opt = {k.upper(): _cast(v[0])}
key = getattr(k, key_modifier)()
opt = {key: _cast(v[0])}
if k.upper() in cls._CACHE_BASE_OPTIONS:
config.update(opt)
else:
config_options.update(opt)
config['OPTIONS'] = config_options

if backend:
config['BACKEND'] = backend

return config

@classmethod
Expand Down
103 changes: 79 additions & 24 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
)


def test_base_options_parsing():
def test_base_options_parsing_memcache():
url = ('memcache://127.0.0.1:11211/?timeout=0&'
'key_prefix=cache_&key_function=foo.get_key&version=1')
url = Env.cache_url_config(url)
Expand All @@ -29,10 +29,29 @@ def test_base_options_parsing():
assert url['TIMEOUT'] == 0
assert url['VERSION'] == 1

url = 'redis://127.0.0.1:6379/?timeout=None'
url = Env.cache_url_config(url)

assert url['TIMEOUT'] is None
@pytest.mark.parametrize('redis_driver,timeout_key',
[
('django.core.cache.backends.redis.RedisCache', 'timeout'),
('django_redis.cache.RedisCache', 'TIMEOUT'),
],
ids=[
'django',
'django_redis',
],
)
def test_base_options_parsing_redis(redis_driver, timeout_key):
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
mocked_cache_schemes.update({
'rediscache': redis_driver,
'redis': redis_driver,
'rediss': redis_driver,
})
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
url = 'redis://127.0.0.1:6379/?timeout=None'
url = Env.cache_url_config(url)

assert url[timeout_key] is None


@pytest.mark.parametrize(
Expand Down Expand Up @@ -134,27 +153,63 @@ def test_rediscache_compat(django_version, django_redis_installed):
else:
assert driver == redis_cache

def test_redis_parsing():
url = ('rediscache://127.0.0.1:6379/1?client_class='
'django_redis.client.DefaultClient&password=secret')
url = Env.cache_url_config(url)

assert url['BACKEND'] == REDIS_DRIVER
assert url['LOCATION'] == 'redis://127.0.0.1:6379/1'
assert url['OPTIONS'] == {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'PASSWORD': 'secret',
}


def test_redis_socket_url():
url = 'redis://:redispass@/path/to/socket.sock?db=0'
url = Env.cache_url_config(url)
assert REDIS_DRIVER == url['BACKEND']
assert url['LOCATION'] == 'unix://:redispass@/path/to/socket.sock'
assert url['OPTIONS'] == {
'DB': 0
}
@pytest.mark.parametrize('redis_driver,client_class_key,password_key',
[
('django.core.cache.backends.redis.RedisCache', 'client_class', 'password'),
('django_redis.cache.RedisCache', 'CLIENT_CLASS', 'PASSWORD'),
],
ids=[
'django',
'django_redis',
],
)
def test_redis_parsing(redis_driver, client_class_key, password_key):
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
mocked_cache_schemes.update({
'rediscache': redis_driver,
'redis': redis_driver,
'rediss': redis_driver,
})
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
url = ('rediscache://127.0.0.1:6379/1?client_class='
'django_redis.client.DefaultClient&password=secret')
url = Env.cache_url_config(url)

assert url['BACKEND'] == redis_driver
assert url['LOCATION'] == 'redis://127.0.0.1:6379/1'
assert url['OPTIONS'] == {
client_class_key: 'django_redis.client.DefaultClient',
password_key: 'secret',
}


@pytest.mark.parametrize('redis_driver,db_key',
[
('django.core.cache.backends.redis.RedisCache', 'db'),
('django_redis.cache.RedisCache', 'DB'),
],
ids=[
'django',
'django_redis',
],
)
def test_redis_socket_url(redis_driver, db_key):
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
mocked_cache_schemes.update({
'rediscache': redis_driver,
'redis': redis_driver,
'rediss': redis_driver,
})
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
url = 'redis://:redispass@/path/to/socket.sock?db=0'
url = Env.cache_url_config(url)

assert url['BACKEND'] == redis_driver
assert url['LOCATION'] == 'unix://:redispass@/path/to/socket.sock'
assert url['OPTIONS'] == {
db_key: 0
}


def test_options_parsing():
Expand Down
37 changes: 26 additions & 11 deletions tests/test_env.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import os
import tempfile
from unittest import mock
from urllib.parse import quote

import pytest
Expand Down Expand Up @@ -352,26 +353,40 @@ def test_db_url_value(self, var, engine, name, host, user, passwd, port):
(Env.DEFAULT_CACHE_ENV,
'django.core.cache.backends.memcached.MemcachedCache',
'127.0.0.1:11211', None),
('CACHE_REDIS', REDIS_DRIVER,
('CACHE_REDIS',
'django.core.cache.backends.redis.RedisCache',
'redis://127.0.0.1:6379/1',
{'client_class': 'django_redis.client.DefaultClient',
'password': 'secret'}),
('CACHE_REDIS',
'django_redis.cache.RedisCache',
'redis://127.0.0.1:6379/1',
{'CLIENT_CLASS': 'django_redis.client.DefaultClient',
'PASSWORD': 'secret'}),
],
ids=[
'memcached',
'redis',
'django', # Django Redis cache backend
'redis_django', # django_redis backend
],
)
def test_cache_url_value(self, var, backend, location, options):
config = self.env.cache_url(var)

assert config['BACKEND'] == backend
assert config['LOCATION'] == location

if options is None:
assert 'OPTIONS' not in config
else:
assert config['OPTIONS'] == options
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
mocked_cache_schemes.update({
'rediscache': backend,
'redis': backend,
'rediss': backend,
})
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
config = self.env.cache_url(var)

assert config['BACKEND'] == backend
assert config['LOCATION'] == location

if options is None:
assert 'OPTIONS' not in config
else:
assert config['OPTIONS'] == options

def test_email_url_value(self):
email_config = self.env.email_url()
Expand Down
2 changes: 2 additions & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ deps =
django40: Django>=4.0,<4.1
django41: Django>=4.1,<4.2
django42: Django>=4.2,<5.0
django50: Django>=5.0,<5.1
django51: Django>=5.1,<5.2
commands_pre =
python -m pip install --upgrade pip
python -m pip install .
Expand Down