Skip to content

Commit e093405

Browse files
committed
Support lower case options for Django Redis cache backend
Closes #517.
1 parent 993b36c commit e093405

File tree

4 files changed

+120
-39
lines changed

4 files changed

+120
-39
lines changed

environ/environ.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -689,19 +689,28 @@ def cache_url_config(cls, url, backend=None):
689689
else:
690690
config['LOCATION'] = locations
691691

692+
if backend:
693+
config['BACKEND'] = backend
694+
692695
if url.query:
693696
config_options = {}
697+
# Django Redis cache backend expects options in lower case
698+
# while "django_redis" expects them in upper case
699+
backend = config['BACKEND']
700+
if backend == 'django.core.cache.backends.redis.RedisCache':
701+
key_modifier = 'lower'
702+
else:
703+
key_modifier = 'upper'
704+
694705
for k, v in parse_qs(url.query).items():
695-
opt = {k.upper(): _cast(v[0])}
706+
key = getattr(k, key_modifier)()
707+
opt = {key: _cast(v[0])}
696708
if k.upper() in cls._CACHE_BASE_OPTIONS:
697709
config.update(opt)
698710
else:
699711
config_options.update(opt)
700712
config['OPTIONS'] = config_options
701713

702-
if backend:
703-
config['BACKEND'] = backend
704-
705714
return config
706715

707716
@classmethod

tests/test_cache.py

Lines changed: 79 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
)
2020

2121

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

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

35-
assert url['TIMEOUT'] is None
33+
@pytest.mark.parametrize('redis_driver,timeout_key',
34+
[
35+
('django.core.cache.backends.redis.RedisCache', 'timeout'),
36+
('django_redis.cache.RedisCache', 'TIMEOUT'),
37+
],
38+
ids=[
39+
'django',
40+
'django_redis',
41+
],
42+
)
43+
def test_base_options_parsing_redis(redis_driver, timeout_key):
44+
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
45+
mocked_cache_schemes.update({
46+
'rediscache': redis_driver,
47+
'redis': redis_driver,
48+
'rediss': redis_driver,
49+
})
50+
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
51+
url = 'redis://127.0.0.1:6379/?timeout=None'
52+
url = Env.cache_url_config(url)
53+
54+
assert url[timeout_key] is None
3655

3756

3857
@pytest.mark.parametrize(
@@ -134,27 +153,63 @@ def test_rediscache_compat(django_version, django_redis_installed):
134153
else:
135154
assert driver == redis_cache
136155

137-
def test_redis_parsing():
138-
url = ('rediscache://127.0.0.1:6379/1?client_class='
139-
'django_redis.client.DefaultClient&password=secret')
140-
url = Env.cache_url_config(url)
141-
142-
assert url['BACKEND'] == REDIS_DRIVER
143-
assert url['LOCATION'] == 'redis://127.0.0.1:6379/1'
144-
assert url['OPTIONS'] == {
145-
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
146-
'PASSWORD': 'secret',
147-
}
148-
149156

150-
def test_redis_socket_url():
151-
url = 'redis://:redispass@/path/to/socket.sock?db=0'
152-
url = Env.cache_url_config(url)
153-
assert REDIS_DRIVER == url['BACKEND']
154-
assert url['LOCATION'] == 'unix://:redispass@/path/to/socket.sock'
155-
assert url['OPTIONS'] == {
156-
'DB': 0
157-
}
157+
@pytest.mark.parametrize('redis_driver,client_class_key,password_key',
158+
[
159+
('django.core.cache.backends.redis.RedisCache', 'client_class', 'password'),
160+
('django_redis.cache.RedisCache', 'CLIENT_CLASS', 'PASSWORD'),
161+
],
162+
ids=[
163+
'django',
164+
'django_redis',
165+
],
166+
)
167+
def test_redis_parsing(redis_driver, client_class_key, password_key):
168+
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
169+
mocked_cache_schemes.update({
170+
'rediscache': redis_driver,
171+
'redis': redis_driver,
172+
'rediss': redis_driver,
173+
})
174+
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
175+
url = ('rediscache://127.0.0.1:6379/1?client_class='
176+
'django_redis.client.DefaultClient&password=secret')
177+
url = Env.cache_url_config(url)
178+
179+
assert url['BACKEND'] == redis_driver
180+
assert url['LOCATION'] == 'redis://127.0.0.1:6379/1'
181+
assert url['OPTIONS'] == {
182+
client_class_key: 'django_redis.client.DefaultClient',
183+
password_key: 'secret',
184+
}
185+
186+
187+
@pytest.mark.parametrize('redis_driver,db_key',
188+
[
189+
('django.core.cache.backends.redis.RedisCache', 'db'),
190+
('django_redis.cache.RedisCache', 'DB'),
191+
],
192+
ids=[
193+
'django',
194+
'django_redis',
195+
],
196+
)
197+
def test_redis_socket_url(redis_driver, db_key):
198+
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
199+
mocked_cache_schemes.update({
200+
'rediscache': redis_driver,
201+
'redis': redis_driver,
202+
'rediss': redis_driver,
203+
})
204+
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
205+
url = 'redis://:redispass@/path/to/socket.sock?db=0'
206+
url = Env.cache_url_config(url)
207+
208+
assert url['BACKEND'] == redis_driver
209+
assert url['LOCATION'] == 'unix://:redispass@/path/to/socket.sock'
210+
assert url['OPTIONS'] == {
211+
db_key: 0
212+
}
158213

159214

160215
def test_options_parsing():

tests/test_env.py

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import os
1010
import tempfile
11+
from unittest import mock
1112
from urllib.parse import quote
1213

1314
import pytest
@@ -352,26 +353,40 @@ def test_db_url_value(self, var, engine, name, host, user, passwd, port):
352353
(Env.DEFAULT_CACHE_ENV,
353354
'django.core.cache.backends.memcached.MemcachedCache',
354355
'127.0.0.1:11211', None),
355-
('CACHE_REDIS', REDIS_DRIVER,
356+
('CACHE_REDIS',
357+
'django.core.cache.backends.redis.RedisCache',
358+
'redis://127.0.0.1:6379/1',
359+
{'client_class': 'django_redis.client.DefaultClient',
360+
'password': 'secret'}),
361+
('CACHE_REDIS',
362+
'django_redis.cache.RedisCache',
356363
'redis://127.0.0.1:6379/1',
357364
{'CLIENT_CLASS': 'django_redis.client.DefaultClient',
358365
'PASSWORD': 'secret'}),
359366
],
360367
ids=[
361368
'memcached',
362-
'redis',
369+
'django', # Django Redis cache backend
370+
'redis_django', # django_redis backend
363371
],
364372
)
365373
def test_cache_url_value(self, var, backend, location, options):
366-
config = self.env.cache_url(var)
367-
368-
assert config['BACKEND'] == backend
369-
assert config['LOCATION'] == location
370-
371-
if options is None:
372-
assert 'OPTIONS' not in config
373-
else:
374-
assert config['OPTIONS'] == options
374+
mocked_cache_schemes = Env.CACHE_SCHEMES.copy()
375+
mocked_cache_schemes.update({
376+
'rediscache': backend,
377+
'redis': backend,
378+
'rediss': backend,
379+
})
380+
with mock.patch.object(Env, 'CACHE_SCHEMES', mocked_cache_schemes):
381+
config = self.env.cache_url(var)
382+
383+
assert config['BACKEND'] == backend
384+
assert config['LOCATION'] == location
385+
386+
if options is None:
387+
assert 'OPTIONS' not in config
388+
else:
389+
assert config['OPTIONS'] == options
375390

376391
def test_email_url_value(self):
377392
email_config = self.env.email_url()

tox.ini

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ deps =
4242
django40: Django>=4.0,<4.1
4343
django41: Django>=4.1,<4.2
4444
django42: Django>=4.2,<5.0
45+
django50: Django>=5.0,<5.1
46+
django51: Django>=5.1,<5.2
4547
commands_pre =
4648
python -m pip install --upgrade pip
4749
python -m pip install .

0 commit comments

Comments
 (0)