[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