[Python-modules-commits] [django-redis] 01/04: Import django-redis_4.7.0.orig.tar.gz
Scott Kitterman
kitterman at moszumanska.debian.org
Fri Jan 6 05:36:34 UTC 2017
This is an automated email from the git hooks/post-receive script.
kitterman pushed a commit to branch master
in repository django-redis.
commit b7f053b3974c66635765aeabbe1903249a89cc4b
Author: Scott Kitterman <scott at kitterman.com>
Date: Fri Jan 6 00:21:53 2017 -0500
Import django-redis_4.7.0.orig.tar.gz
---
PKG-INFO | 2 +-
django_redis.egg-info/PKG-INFO | 2 +-
django_redis/__init__.py | 2 +-
django_redis/cache.py | 3 +
django_redis/client/default.py | 79 +++++++++++++++----------
django_redis/client/sharded.py | 8 ++-
doc/content.adoc | 8 +--
doc/dist/latest/index.html | 47 +++++++++------
tests/redis_backend_testapp/tests.py | 111 ++++++++++++++++++++++++++++++++++-
9 files changed, 201 insertions(+), 61 deletions(-)
diff --git a/PKG-INFO b/PKG-INFO
index 63c7086..ea0683f 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-redis
-Version: 4.6.0
+Version: 4.7.0
Summary: Full featured redis cache backend for Django.
Home-page: https://github.com/niwibe/django-redis
Author: Andrei Antoukh
diff --git a/django_redis.egg-info/PKG-INFO b/django_redis.egg-info/PKG-INFO
index 63c7086..ea0683f 100644
--- a/django_redis.egg-info/PKG-INFO
+++ b/django_redis.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: django-redis
-Version: 4.6.0
+Version: 4.7.0
Summary: Full featured redis cache backend for Django.
Home-page: https://github.com/niwibe/django-redis
Author: Andrei Antoukh
diff --git a/django_redis/__init__.py b/django_redis/__init__.py
index 6a44444..69eb021 100644
--- a/django_redis/__init__.py
+++ b/django_redis/__init__.py
@@ -1,6 +1,6 @@
# -*- coding: utf-8 -*-
-VERSION = (4, 6, 0)
+VERSION = (4, 7, 0)
__version__ = '.'.join(map(str, VERSION))
diff --git a/django_redis/cache.py b/django_redis/cache.py
index d584ae2..5d581aa 100644
--- a/django_redis/cache.py
+++ b/django_redis/cache.py
@@ -8,9 +8,11 @@ from django.core.cache.backends.base import BaseCache
from .util import load_class
from .exceptions import ConnectionInterrupted
+
DJANGO_REDIS_IGNORE_EXCEPTIONS = getattr(settings, "DJANGO_REDIS_IGNORE_EXCEPTIONS", False)
DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS = getattr(settings, "DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS", False)
DJANGO_REDIS_LOGGER = getattr(settings, "DJANGO_REDIS_LOGGER", False)
+DJANGO_REDIS_SCAN_ITERSIZE = getattr(settings, "DJANGO_REDIS_SCAN_ITERSIZE", 10)
if DJANGO_REDIS_LOG_IGNORED_EXCEPTIONS:
@@ -92,6 +94,7 @@ class RedisCache(BaseCache):
@omit_exception
def delete_pattern(self, *args, **kwargs):
+ kwargs['itersize'] = kwargs.get('itersize', DJANGO_REDIS_SCAN_ITERSIZE)
return self.client.delete_pattern(*args, **kwargs)
@omit_exception
diff --git a/django_redis/client/default.py b/django_redis/client/default.py
index e97b1c1..f57a266 100644
--- a/django_redis/client/default.py
+++ b/django_redis/client/default.py
@@ -46,6 +46,7 @@ class DefaultClient(object):
self._clients = [None] * len(self._server)
self._options = params.get("OPTIONS", {})
+ self._slave_read_only = self._options.get('SLAVE_READ_ONLY', True)
serializer_path = self._options.get("SERIALIZER", "django_redis.serializers.pickle.PickleSerializer")
serializer_cls = load_class(serializer_path)
@@ -61,7 +62,7 @@ class DefaultClient(object):
def __contains__(self, key):
return self.has_key(key)
- def get_next_client_index(self, write=True):
+ def get_next_client_index(self, write=True, tried=()):
"""
Return a next index for read client.
This function implements a default behavior for
@@ -70,12 +71,16 @@ class DefaultClient(object):
Overwrite this function if you want a specific
behavior.
"""
+ if tried and len(tried) < len(self._server):
+ not_tried = [i for i in range(0, len(self._server)) if i not in tried]
+ return random.choice(not_tried)
+
if write or len(self._server) == 1:
return 0
return random.randint(1, len(self._server) - 1)
- def get_client(self, write=True):
+ def get_client(self, write=True, tried=(), show_index=False):
"""
Method used for obtain a raw redis client.
@@ -83,12 +88,15 @@ class DefaultClient(object):
operations for obtain a native redis client/connection
instance.
"""
- index = self.get_next_client_index(write=write)
+ index = self.get_next_client_index(write=write, tried=tried or [])
if self._clients[index] is None:
self._clients[index] = self.connect(index)
- return self._clients[index]
+ if show_index:
+ return self._clients[index], index
+ else:
+ return self._clients[index]
def connect(self, index=0):
"""
@@ -103,10 +111,6 @@ class DefaultClient(object):
Persist a value to the cache, and set an optional expiration time.
Also supports optional nx parameter. If set to True - will use redis setnx instead of set.
"""
-
- if not client:
- client = self.get_client(write=True)
-
nkey = self.make_key(key, version=version)
nvalue = self.encode(value)
@@ -117,26 +121,36 @@ class DefaultClient(object):
if timeout == DEFAULT_TIMEOUT:
timeout = self._backend.default_timeout
- try:
- if timeout is not None:
- if timeout > 0:
- # Convert to milliseconds
- timeout = int(timeout * 1000)
- elif timeout <= 0:
- if nx:
- # Using negative timeouts when nx is True should
- # not expire (in our case delete) the value if it exists.
- # Obviously expire not existent value is noop.
- timeout = None
- else:
- # redis doesn't support negative timeouts in ex flags
- # so it seems that it's better to just delete the key
- # than to set it and than expire in a pipeline
- return self.delete(key, client=client, version=version)
-
- return client.set(nkey, nvalue, nx=nx, px=timeout, xx=xx)
- except _main_exceptions as e:
- raise ConnectionInterrupted(connection=client, parent=e)
+ original_client = client
+ tried = []
+ while True:
+ try:
+ if not client:
+ client, index = self.get_client(write=True, tried=tried, show_index=True)
+
+ if timeout is not None:
+ if timeout > 0:
+ # Convert to milliseconds
+ timeout = int(timeout * 1000)
+ elif timeout <= 0:
+ if nx:
+ # Using negative timeouts when nx is True should
+ # not expire (in our case delete) the value if it exists.
+ # Obviously expire not existent value is noop.
+ timeout = None
+ else:
+ # redis doesn't support negative timeouts in ex flags
+ # so it seems that it's better to just delete the key
+ # than to set it and than expire in a pipeline
+ return self.delete(key, client=client, version=version)
+
+ return client.set(nkey, nvalue, nx=nx, px=timeout, xx=xx)
+ except _main_exceptions as e:
+ if not original_client and not self._slave_read_only and len(tried) < len(self._server):
+ tried.append(index)
+ client = None
+ continue
+ raise ConnectionInterrupted(connection=client, parent=e)
def incr_version(self, key, delta=1, version=None, client=None):
"""
@@ -239,7 +253,7 @@ class DefaultClient(object):
except _main_exceptions as e:
raise ConnectionInterrupted(connection=client, parent=e)
- def delete_pattern(self, pattern, version=None, prefix=None, client=None):
+ def delete_pattern(self, pattern, version=None, prefix=None, client=None, itersize=None):
"""
Remove all keys matching pattern.
"""
@@ -248,9 +262,14 @@ class DefaultClient(object):
client = self.get_client(write=True)
pattern = self.make_key(pattern, version=version, prefix=prefix)
+
+ kwargs = {'match': pattern, }
+ if itersize:
+ kwargs['count'] = itersize
+
try:
count = 0
- for key in client.scan_iter(pattern):
+ for key in client.scan_iter(**kwargs):
client.delete(key)
count += 1
return count
diff --git a/django_redis/client/sharded.py b/django_redis/client/sharded.py
index 4162438..3ba2aee 100644
--- a/django_redis/client/sharded.py
+++ b/django_redis/client/sharded.py
@@ -238,16 +238,20 @@ class ShardClient(DefaultClient):
decoded_keys = (smart_text(k) for k in keys)
return [self.reverse_key(k) for k in decoded_keys]
- def delete_pattern(self, pattern, version=None, client=None):
+ def delete_pattern(self, pattern, version=None, client=None, itersize=None):
"""
Remove all keys matching pattern.
"""
pattern = self.make_key(pattern, version=version)
+ kwargs = {'match': pattern, }
+ if itersize:
+ kwargs['count'] = itersize
+
keys = []
for server, connection in self._serverdict.items():
- keys.extend([key for key in connection.scan_iter(pattern)])
+ keys.extend([key for key in connection.scan_iter(**kwargs)])
res = 0
if keys:
diff --git a/doc/content.adoc b/doc/content.adoc
index 9b40558..eaab56a 100644
--- a/doc/content.adoc
+++ b/doc/content.adoc
@@ -1,7 +1,7 @@
django-redis documentation
==========================
Andrey Antukh, <niwi at niwi.be>
-4.6.0
+4.7.0
:toc: left
:numbered:
:source-highlighter: pygments
@@ -31,14 +31,14 @@ Because:
- Facilities for raw access to Redis client/connection pool.
- Highly configurable (can emulate memcached exception behavior, for example).
- Unix sockets supported by default.
-- With support for python 2.7, 3.4 and 3.5
+- With support for python 2.7, 3.4, 3.5 and 3.6
Supported django-redis versions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- Supported stable version: *4.6.0*
+- Supported stable version: *4.7.0*
- Supported stable version: *3.8.4*
@@ -60,7 +60,7 @@ Django version support
^^^^^^^^^^^^^^^^^^^^^^
- *django-redis 3.8.x* will maintain support for django 1.4, 1.5, 1.6, 1.7 (and maybe 1.8)
-- *django-redis 4.4.x* will maintain support for django 1.6, 1.7, 1.8 and 1.9
+- *django-redis 4.4.x* will maintain support for django 1.6, 1.7, 1.8, 1.9 and 1.10
Redis Server Support
diff --git a/doc/dist/latest/index.html b/doc/dist/latest/index.html
index 552f060..1bb3ad4 100644
--- a/doc/dist/latest/index.html
+++ b/doc/dist/latest/index.html
@@ -4,12 +4,12 @@
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
-<meta name="generator" content="Asciidoctor 1.5.4">
+<meta name="generator" content="Asciidoctor 1.5.5">
<meta name="author" content="Andrey Antukh, <niwi at niwi.be>">
<title>django-redis documentation</title>
<style>
.listingblock .pygments .hll { background-color: #ffffcc }
-.listingblock .pygments { background: #f0f0f0; }
+.listingblock .pygments, .listingblock .pygments code { background: #f0f0f0; }
.listingblock .pygments .tok-c { color: #60a0b0; font-style: italic } /* Comment */
.listingblock .pygments .tok-err { border: 1px solid #FF0000 } /* Error */
.listingblock .pygments .tok-k { color: #007020; font-weight: bold } /* Keyword */
@@ -80,7 +80,7 @@
<h1>django-redis documentation</h1>
<div class="details">
<span id="author" class="author">Andrey Antukh, <niwi at niwi.be></span><br>
-<span id="revdate">4.4.4</span>
+<span id="revdate">4.6.0</span>
</div>
<div id="toc" class="toc2">
<div id="toctitle">Table of Contents</div>
@@ -187,7 +187,7 @@
<div class="ulist">
<ul>
<li>
-<p>Supported stable version: <strong>4.4.4</strong></p>
+<p>Supported stable version: <strong>4.6.0</strong></p>
</li>
<li>
<p>Supported stable version: <strong>3.8.4</strong></p>
@@ -217,7 +217,7 @@ fix releases and are should contain only bug fixes. No new features.</p>
<p><strong>django-redis 3.8.x</strong> will maintain support for django 1.4, 1.5, 1.6, 1.7 (and maybe 1.8)</p>
</li>
<li>
-<p><strong>django-redis 4.4.x</strong> will maintain support for django 1.6, 1.7, 1.8 and 1.9</p>
+<p><strong>django-redis 4.4.x</strong> will maintain support for django 1.6, 1.7, 1.8, 1.9 and 1.10</p>
</li>
</ul>
</div>
@@ -317,19 +317,28 @@ unix://[:password]@/path/to/socket.sock?db=0</pre>
</li>
</ul>
</div>
-<div class="admonitionblock note">
-<table>
-<tr>
-<td class="icon">
-<div class="title">Note</div>
-</td>
-<td class="content">
-if you are coming fron django-redis < 3.8.x, you are probably using redis_cache. Since
-django-redis 3.8.x, <code>redis_cache</code> module is deprecated in favor to <code>django_redis</code>. The
-<code>redis_cache</code> module will be removed in 3.9.x versions.
-</td>
-</tr>
-</table>
+<div class="paragraph">
+<p>In some circumstances the password you should use to connect redis
+is not URL-safe, in this case you can escape it or just use the
+convenience option in <code>OPTIONS</code> dict:</p>
+</div>
+<div class="listingblock">
+<div class="content">
+<pre class="pygments highlight"><code data-lang="python"><span class="tok-n">CACHES</span> <span class="tok-o">=</span> <span class="tok-p">{</span>
+ <span class="tok-s">"default"</span><span class="tok-p">:</span> <span class="tok-p">{</span>
+ <span class="tok-s">"BACKEND"</span><span class="tok-p">:</span> <span class="tok-s">"django_redis.cache.RedisCache"</span><span class="tok-p">,</span>
+ <span class="tok-s">"LOCATION"</span><span class="tok-p">:</span> <span class="tok-s">"redis://127.0.0.1:6379/1"</span><span class="tok-p">,</span>
+ <span class="tok-s">"OPTIONS"</span><span class="tok-p">:</span> <span class="tok-p">{</span>
+ <span class="tok-s">"CLIENT_CLASS"</span><span class="tok-p">:</span> <span class="tok-s">"django_redis.client.DefaultClient"</span><span class="tok-p">,</span>
+ <span class="tok-s">"PASSWORD"</span><span class="tok-p">:</span> <span class="tok-s">"mysecret"</span>
+ <span class="tok-p">}</span>
+ <span class="tok-p">}</span>
+<span class="tok-p">}</span></code></pre>
+</div>
+</div>
+<div class="paragraph">
+<p>Take care, that this option does not overwrites the password in the uri, so if you
+have set the password in the uri, this settings will be ignored.</p>
</div>
</div>
<div class="sect2">
@@ -1083,7 +1092,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</code></pre>
</div>
<div id="footer">
<div id="footer-text">
-Last updated 2016-07-25 15:22:36 EEST
+Last updated 2017-01-02 09:46:57 CET
</div>
</div>
</body>
diff --git a/tests/redis_backend_testapp/tests.py b/tests/redis_backend_testapp/tests.py
index 6f1ebc6..3f49377 100644
--- a/tests/redis_backend_testapp/tests.py
+++ b/tests/redis_backend_testapp/tests.py
@@ -3,7 +3,6 @@
from __future__ import absolute_import, unicode_literals, print_function
import base64
-import os
import unittest
import sys
import time
@@ -15,13 +14,19 @@ try:
except ImportError:
from mock import patch
+from mock import Mock
+
from django.conf import settings
-from django.core.cache import cache, caches
+from django.core.cache import cache
from django import VERSION
from django.test import TestCase
+import fakeredis
+
import django_redis.cache
from django_redis import pool
+from django_redis.client import DefaultClient
+from django_redis.client import ShardClient
from django_redis.client import herd
from django_redis.serializers import json as json_serializer
@@ -437,7 +442,7 @@ class DjangoRedisCacheTests(TestCase):
print(e)
def test_delete_pattern(self):
- for key in ["foo-aa","foo-ab", "foo-bb","foo-bc"]:
+ for key in ["foo-aa", "foo-ab", "foo-bb", "foo-bc"]:
self.cache.set(key, "foo")
res = self.cache.delete_pattern("*foo-a*")
@@ -449,6 +454,25 @@ class DjangoRedisCacheTests(TestCase):
res = self.cache.delete_pattern("*foo-a*")
self.assertFalse(bool(res))
+ @patch('django_redis.cache.RedisCache.client')
+ def test_delete_pattern_with_custom_count(self, client_mock):
+ for key in ["foo-aa", "foo-ab", "foo-bb", "foo-bc"]:
+ self.cache.set(key, "foo")
+
+ self.cache.delete_pattern("*foo-a*", itersize=2)
+
+ client_mock.delete_pattern.assert_called_once_with("*foo-a*", itersize=2)
+
+ @patch('django_redis.cache.RedisCache.client')
+ def test_delete_pattern_with_settings_default_scan_count(self, client_mock):
+ for key in ["foo-aa", "foo-ab", "foo-bb", "foo-bc"]:
+ self.cache.set(key, "foo")
+ expected_count = django_redis.cache.DJANGO_REDIS_SCAN_ITERSIZE
+
+ self.cache.delete_pattern("*foo-a*")
+
+ client_mock.delete_pattern.assert_called_once_with("*foo-a*", itersize=expected_count)
+
def test_close(self):
cache = caches["default"]
cache.set("f", "1")
@@ -902,3 +926,84 @@ class SessionTests(SessionTestsMixin, TestCase):
def test_actual_expiry(self):
pass
+
+
+class TestDefaultClient(TestCase):
+
+ @patch('redis_backend_testapp.tests.DefaultClient.get_client')
+ @patch('redis_backend_testapp.tests.DefaultClient.__init__', return_value=None)
+ def test_delete_pattern_calls_get_client_given_no_client(self, init_mock, get_client_mock):
+ client = DefaultClient()
+ client._backend = Mock()
+
+ client.delete_pattern(pattern='foo*')
+
+ get_client_mock.assert_called_once_with(write=True)
+
+ @patch('redis_backend_testapp.tests.DefaultClient.make_key')
+ @patch('redis_backend_testapp.tests.DefaultClient.__init__', return_value=None)
+ def test_delete_pattern_calls_make_key(self, init_mock, make_key_mock):
+ client = DefaultClient()
+ client._backend = Mock()
+ redis_client = fakeredis.FakeStrictRedis()
+ client.delete_pattern(pattern='foo*', client=redis_client)
+
+ make_key_mock.assert_called_once_with('foo*', version=None, prefix=None)
+
+ @patch('redis_backend_testapp.tests.DefaultClient.make_key')
+ @patch('redis_backend_testapp.tests.DefaultClient.get_client', return_value=Mock())
+ @patch('redis_backend_testapp.tests.DefaultClient.__init__', return_value=None)
+ def test_delete_pattern_calls_scan_iter_with_count_if_itersize_given(
+ self, init_mock, get_client_mock, make_key_mock):
+ client = DefaultClient()
+ client._backend = Mock()
+ get_client_mock.return_value.scan_iter.return_value = []
+
+ client.delete_pattern(pattern='foo*', itersize=90210)
+
+ get_client_mock.return_value.scan_iter.assert_called_once_with(
+ count=90210, match=make_key_mock.return_value)
+
+
+class TestShardClient(TestCase):
+
+ @patch('redis_backend_testapp.tests.DefaultClient.make_key')
+ @patch('redis_backend_testapp.tests.ShardClient.__init__', return_value=None)
+ def test_delete_pattern_calls_scan_iter_with_count_if_itersize_given(
+ self, init_mock, make_key_mock):
+ client = ShardClient()
+ client._backend = Mock()
+ connection = Mock()
+ connection.scan_iter.return_value = []
+ client._serverdict = {'test': connection}
+
+ client.delete_pattern(pattern='foo*', itersize=10)
+
+ connection.scan_iter.assert_called_once_with(count=10, match=make_key_mock.return_value)
+
+ @patch('redis_backend_testapp.tests.DefaultClient.make_key')
+ @patch('redis_backend_testapp.tests.ShardClient.__init__', return_value=None)
+ def test_delete_pattern_calls_scan_iter(self, init_mock, make_key_mock):
+ client = ShardClient()
+ client._backend = Mock()
+ connection = Mock()
+ connection.scan_iter.return_value = []
+ client._serverdict = {'test': connection}
+
+ client.delete_pattern(pattern='foo*')
+
+ connection.scan_iter.assert_called_once_with(match=make_key_mock.return_value)
+
+ @patch('redis_backend_testapp.tests.DefaultClient.make_key')
+ @patch('redis_backend_testapp.tests.ShardClient.__init__', return_value=None)
+ def test_delete_pattern_calls_delete_for_given_keys(self, init_mock, make_key_mock):
+ client = ShardClient()
+ client._backend = Mock()
+ connection = Mock()
+ connection.scan_iter.return_value = [Mock(), Mock()]
+ connection.delete.return_value = 0
+ client._serverdict = {'test': connection}
+
+ client.delete_pattern(pattern='foo*')
+
+ connection.delete.assert_called_once_with(*connection.scan_iter.return_value)
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/django-redis.git
More information about the Python-modules-commits
mailing list