[Python-modules-commits] [aioredis] 01/03: Import aioredis_0.2.6.orig.tar.gz
Piotr Ożarowski
piotr at moszumanska.debian.org
Thu Apr 14 18:56:40 UTC 2016
This is an automated email from the git hooks/post-receive script.
piotr pushed a commit to branch master
in repository aioredis.
commit 85fc2e91b133f1849faf4fadbdcd95d1a9287f56
Author: Piotr Ożarowski <piotr at debian.org>
Date: Thu Apr 14 20:52:48 2016 +0200
Import aioredis_0.2.6.orig.tar.gz
---
CHANGES.txt | 23 ++++++++++++++
PKG-INFO | 26 +++++++++++++++-
README.rst | 1 +
aioredis.egg-info/PKG-INFO | 26 +++++++++++++++-
aioredis.egg-info/requires.txt | 2 +-
aioredis/__init__.py | 2 +-
aioredis/commands/__init__.py | 7 +++--
aioredis/commands/server.py | 65 +++++++++++++++++++++++++++++++++++-----
aioredis/commands/transaction.py | 45 ++++++++++++++--------------
aioredis/connection.py | 47 ++++++++++++++++-------------
aioredis/pool.py | 8 +++--
aioredis/util.py | 20 +++++++++++++
setup.cfg | 2 +-
13 files changed, 212 insertions(+), 62 deletions(-)
diff --git a/CHANGES.txt b/CHANGES.txt
index c703b17..ac45c9f 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,6 +1,29 @@
Changes
-------
+0.2.6 (2016-03-30)
+^^^^^^^^^^^^^^^^^^
+
+* Fixed Multi/Exec transactions cancellation issue
+ (see `#110 <https://github.com/aio-libs/aioredis/issues/110>`_
+ and `#114 <https://github.com/aio-libs/aioredis/issues/114>`_);
+
+* Fixed Pub/Sub subscribe concurrency issue
+ (see `#113 <https://github.com/aio-libs/aioredis/issues/113>`_
+ and `#115 <https://github.com/aio-libs/aioredis/issues/115>`_);
+
+* Add SSL/TLS support
+ (see `#116 <https://github.com/aio-libs/aioredis/issues/116>`_);
+
+* ``aioredis.ConnectionClosedError`` raised in ``execute_pubsub`` as well
+ (see `#108 <https://github.com/aio-libs/aioredis/issues/108>`_);
+
+* ``Redis.slaveof()`` method signature changed: now to disable
+ replication one should call ``redis.slaveof(None)`` instead of ``redis.slaveof()``;
+
+* More tests added;
+
+
0.2.5 (2016-03-02)
^^^^^^^^^^^^^^^^^^
diff --git a/PKG-INFO b/PKG-INFO
index ea61354..ab189d1 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: aioredis
-Version: 0.2.5
+Version: 0.2.6
Summary: asyncio (PEP 3156) Redis support
Home-page: https://github.com/aio-libs/aioredis
Author: Alexey Popravka
@@ -28,6 +28,7 @@ Description: aioredis
Connections Pool Yes
Pipelining support Yes
Pub/Sub support Yes
+ SSL/TLS support Yes
Redis Cluster support WIP
Trollius (python 2.7) No
Tested python versions `3.3, 3.4, 3.5`_
@@ -138,6 +139,29 @@ Description: aioredis
Changes
-------
+ 0.2.6 (2016-03-30)
+ ^^^^^^^^^^^^^^^^^^
+
+ * Fixed Multi/Exec transactions cancellation issue
+ (see `#110 <https://github.com/aio-libs/aioredis/issues/110>`_
+ and `#114 <https://github.com/aio-libs/aioredis/issues/114>`_);
+
+ * Fixed Pub/Sub subscribe concurrency issue
+ (see `#113 <https://github.com/aio-libs/aioredis/issues/113>`_
+ and `#115 <https://github.com/aio-libs/aioredis/issues/115>`_);
+
+ * Add SSL/TLS support
+ (see `#116 <https://github.com/aio-libs/aioredis/issues/116>`_);
+
+ * ``aioredis.ConnectionClosedError`` raised in ``execute_pubsub`` as well
+ (see `#108 <https://github.com/aio-libs/aioredis/issues/108>`_);
+
+ * ``Redis.slaveof()`` method signature changed: now to disable
+ replication one should call ``redis.slaveof(None)`` instead of ``redis.slaveof()``;
+
+ * More tests added;
+
+
0.2.5 (2016-03-02)
^^^^^^^^^^^^^^^^^^
diff --git a/README.rst b/README.rst
index c1f01a2..f77a5c0 100644
--- a/README.rst
+++ b/README.rst
@@ -20,6 +20,7 @@ Low-level & High-level APIs Yes
Connections Pool Yes
Pipelining support Yes
Pub/Sub support Yes
+SSL/TLS support Yes
Redis Cluster support WIP
Trollius (python 2.7) No
Tested python versions `3.3, 3.4, 3.5`_
diff --git a/aioredis.egg-info/PKG-INFO b/aioredis.egg-info/PKG-INFO
index ea61354..ab189d1 100644
--- a/aioredis.egg-info/PKG-INFO
+++ b/aioredis.egg-info/PKG-INFO
@@ -1,6 +1,6 @@
Metadata-Version: 1.1
Name: aioredis
-Version: 0.2.5
+Version: 0.2.6
Summary: asyncio (PEP 3156) Redis support
Home-page: https://github.com/aio-libs/aioredis
Author: Alexey Popravka
@@ -28,6 +28,7 @@ Description: aioredis
Connections Pool Yes
Pipelining support Yes
Pub/Sub support Yes
+ SSL/TLS support Yes
Redis Cluster support WIP
Trollius (python 2.7) No
Tested python versions `3.3, 3.4, 3.5`_
@@ -138,6 +139,29 @@ Description: aioredis
Changes
-------
+ 0.2.6 (2016-03-30)
+ ^^^^^^^^^^^^^^^^^^
+
+ * Fixed Multi/Exec transactions cancellation issue
+ (see `#110 <https://github.com/aio-libs/aioredis/issues/110>`_
+ and `#114 <https://github.com/aio-libs/aioredis/issues/114>`_);
+
+ * Fixed Pub/Sub subscribe concurrency issue
+ (see `#113 <https://github.com/aio-libs/aioredis/issues/113>`_
+ and `#115 <https://github.com/aio-libs/aioredis/issues/115>`_);
+
+ * Add SSL/TLS support
+ (see `#116 <https://github.com/aio-libs/aioredis/issues/116>`_);
+
+ * ``aioredis.ConnectionClosedError`` raised in ``execute_pubsub`` as well
+ (see `#108 <https://github.com/aio-libs/aioredis/issues/108>`_);
+
+ * ``Redis.slaveof()`` method signature changed: now to disable
+ replication one should call ``redis.slaveof(None)`` instead of ``redis.slaveof()``;
+
+ * More tests added;
+
+
0.2.5 (2016-03-02)
^^^^^^^^^^^^^^^^^^
diff --git a/aioredis.egg-info/requires.txt b/aioredis.egg-info/requires.txt
index ebc4fdc..01e7805 100644
--- a/aioredis.egg-info/requires.txt
+++ b/aioredis.egg-info/requires.txt
@@ -1 +1 @@
-hiredis
\ No newline at end of file
+hiredis
diff --git a/aioredis/__init__.py b/aioredis/__init__.py
index b7e7d09..be5fd0e 100644
--- a/aioredis/__init__.py
+++ b/aioredis/__init__.py
@@ -12,7 +12,7 @@ from .errors import (
)
-__version__ = '0.2.5'
+__version__ = '0.2.6'
# make pyflakes happy
(create_connection, RedisConnection,
diff --git a/aioredis/commands/__init__.py b/aioredis/commands/__init__.py
index 170be91..abff333 100644
--- a/aioredis/commands/__init__.py
+++ b/aioredis/commands/__init__.py
@@ -126,7 +126,7 @@ class Redis(GenericCommandsMixin, StringCommandsMixin,
@asyncio.coroutine
-def create_redis(address, *, db=None, password=None,
+def create_redis(address, *, db=None, password=None, ssl=None,
encoding=None, commands_factory=Redis,
loop=None):
"""Creates high-level Redis interface.
@@ -135,13 +135,14 @@ def create_redis(address, *, db=None, password=None,
"""
conn = yield from create_connection(address, db=db,
password=password,
+ ssl=ssl,
encoding=encoding,
loop=loop)
return commands_factory(conn)
@asyncio.coroutine
-def create_reconnecting_redis(address, *, db=None, password=None,
+def create_reconnecting_redis(address, *, db=None, password=None, ssl=None,
encoding=None, commands_factory=Redis,
loop=None):
"""Creates high-level Redis interface.
@@ -152,7 +153,7 @@ def create_reconnecting_redis(address, *, db=None, password=None,
# a first connection in it, or just resolve DNS. So let's keep it
# coroutine for forward compatibility
conn = AutoConnector(address,
- db=db, password=password,
+ db=db, password=password, ssl=ssl,
encoding=encoding, loop=loop)
return commands_factory(conn)
diff --git a/aioredis/commands/server.py b/aioredis/commands/server.py
index 25eaf86..76e68f5 100644
--- a/aioredis/commands/server.py
+++ b/aioredis/commands/server.py
@@ -1,6 +1,7 @@
from collections import namedtuple
from aioredis.util import wait_ok, wait_convert, wait_make_dict, _NOTSET
+from aioredis.log import logger
class ServerCommandsMixin:
@@ -30,7 +31,10 @@ class ServerCommandsMixin:
raise NotImplementedError
def client_list(self):
- """Get the list of client connections."""
+ """Get the list of client connections.
+
+ Returns list of ClientInfo named tuples.
+ """
fut = self._conn.execute(b'CLIENT', b'LIST', encoding='utf-8')
return wait_convert(fut, to_tuples)
@@ -56,11 +60,17 @@ class ServerCommandsMixin:
fut = self._conn.execute(b'CLIENT', b'SETNAME', name)
return wait_ok(fut)
- def config_get(self, parameter):
- """Get the value of a configuration parameter."""
+ def config_get(self, parameter='*'):
+ """Get the value of a configuration parameter(s).
+
+ If called without argument will return all parameters.
+
+ :raises TypeError: if parameter is not string
+ """
if not isinstance(parameter, str):
raise TypeError("parameter must be str")
- fut = self._conn.execute(b'CONFIG', b'GET', parameter)
+ fut = self._conn.execute(b'CONFIG', b'GET', parameter,
+ encoding='utf-8')
return wait_make_dict(fut)
def config_rewrite(self):
@@ -122,8 +132,13 @@ class ServerCommandsMixin:
raise NotImplementedError
def role(self):
- """Return the role of the instance in the context of replication."""
- return self._conn.execute(b'ROLE')
+ """Return the role of the server instance.
+
+ Returns named tuples describing role of the instance.
+ For fields information see http://redis.io/commands/role#output-format
+ """
+ fut = self._conn.execute(b'ROLE', encoding='utf-8')
+ return wait_convert(fut, parse_role)
def save(self):
"""Synchronously save the dataset to disk."""
@@ -140,12 +155,21 @@ class ServerCommandsMixin:
else:
return self._conn.execute(b'SHUTDOWN')
- def slaveof(self, host=None, port=None):
+ def slaveof(self, host=_NOTSET, port=None):
"""Make the server a slave of another instance,
or promote it as master.
- Calling slaveof without arguments will send ``SLAVEOF NO ONE``.
+ Calling ``slaveof(None)`` will send ``SLAVEOF NO ONE``.
+
+ .. versionchanged:: v0.2.6
+ ``slaveof()`` form deprecated
+ in favour of explicit ``slaveof(None)``.
"""
+ if host is _NOTSET:
+ logger.warning("slaveof() form is deprecated!"
+ " Use slaveof(None) to turn redis into a MASTER.")
+ host = None
+ # TODO: drop in 0.3.0
if host is None and port is None:
return self._conn.execute(b'SLAVEOF', b'NO', b'ONE')
return self._conn.execute(b'SLAVEOF', host, port)
@@ -187,6 +211,7 @@ def to_tuples(value):
line = next(lines)
line = list(map(_split, line.split(' ')))
ClientInfo = namedtuple('ClientInfo', ' '.join(k for k, v in line))
+ # TODO: parse flags and other known fields
result = [ClientInfo(**dict(line))]
for line in lines:
result.append(ClientInfo(**dict(map(_split, line.split(' ')))))
@@ -205,3 +230,27 @@ def parse_info(info):
value = dict(map(lambda i: i.split('='), value.split(',')))
tmp[key] = value
return res
+
+
+# XXX: may change in future
+# (may be hard to maintain for new/old redis versions)
+MasterInfo = namedtuple('MasterInfo', 'role replication_offset slaves')
+MasterSlaveInfo = namedtuple('MasterSlaveInfo', 'ip port ack_offset')
+
+SlaveInfo = namedtuple('SlaveInfo',
+ 'role master_ip master_port state received')
+
+SentinelInfo = namedtuple('SentinelInfo', 'masters')
+
+
+def parse_role(role):
+ type_ = role[0]
+ if type_ == 'master':
+ slaves = [MasterSlaveInfo(s[0], int(s[1]), int(s[2]))
+ for s in role[2]]
+ return MasterInfo(role[0], int(role[1]), slaves)
+ elif type_ == 'slave':
+ return SlaveInfo(role[0], role[1], int(role[2]), role[3], int(role[4]))
+ elif type_ == 'sentinel':
+ return SentinelInfo(*role)
+ return role
diff --git a/aioredis/commands/transaction.py b/aioredis/commands/transaction.py
index 505b36b..f177401 100644
--- a/aioredis/commands/transaction.py
+++ b/aioredis/commands/transaction.py
@@ -199,10 +199,7 @@ class Pipeline:
elif fut.exception():
waiter.set_exception(fut.exception())
else:
- self._set_result(fut, waiter)
-
- def _set_result(self, fut, waiter):
- waiter.set_result(fut.result())
+ waiter.set_result(fut.result())
class MultiExec(Pipeline):
@@ -249,15 +246,19 @@ class MultiExec(Pipeline):
multi = conn.execute('MULTI')
coros = list(self._send_pipeline(conn))
exec_ = conn.execute('EXEC')
+ gather = asyncio.gather(multi, *coros, loop=self._loop,
+ return_exceptions=True)
try:
- yield from asyncio.gather(multi, *coros,
- loop=self._loop)
+ yield from asyncio.shield(gather, loop=self._loop)
except asyncio.CancelledError:
- pass
+ yield from gather
finally:
- if self._conn.closed:
+ if conn.closed:
for fut in waiters:
fut.cancel()
+ for fut in self._results:
+ if not fut.done():
+ fut.cancel()
else:
try:
results = yield from exec_
@@ -265,10 +266,10 @@ class MultiExec(Pipeline):
for fut in waiters:
fut.set_exception(err)
else:
- assert len(results) == len(waiters), (results, waiters)
+ assert len(results) == len(waiters), (
+ "Results does not match waiters", results, waiters)
self._resolve_waiters(results, return_exceptions)
- return (yield from self._gather_result(
- return_exceptions))
+ return (yield from self._gather_result(return_exceptions))
def _resolve_waiters(self, results, return_exceptions):
errors = []
@@ -281,15 +282,13 @@ class MultiExec(Pipeline):
if errors and not return_exceptions:
raise MultiExecError(errors)
- def _set_result(self, fut, waiter):
- # fut is done and must be 'QUEUED'
- if fut in self._waiters:
- self._waiters.remove(fut)
- waiter.set_result(fut.result())
- elif fut.result() not in {b'QUEUED', 'QUEUED'}:
- waiter.set_result(fut.result())
- else:
- fut = asyncio.Future(loop=self._loop)
- self._waiters.append(fut)
- fut.add_done_callback(
- functools.partial(self._check_result, waiter=waiter))
+ def _check_result(self, fut, waiter):
+ assert waiter not in self._waiters, (fut, waiter, self._waiters)
+ assert not waiter.done(), waiter
+ if fut.cancelled(): # yield from gather was cancelled
+ waiter.cancel()
+ elif fut.exception(): # server replied with error
+ waiter.set_exception(fut.exception())
+ elif fut.result() in {b'QUEUED', 'QUEUED'}:
+ # got result, it should be QUEUED
+ self._waiters.append(waiter)
diff --git a/aioredis/connection.py b/aioredis/connection.py
index 05bc266..d5abd29 100644
--- a/aioredis/connection.py
+++ b/aioredis/connection.py
@@ -7,7 +7,10 @@ from collections import deque
from .util import (
encode_command,
- wait_ok, _NOTSET,
+ wait_ok,
+ _NOTSET,
+ _set_result,
+ _set_exception,
coerced_keys_dict,
Channel,
decode,
@@ -35,7 +38,7 @@ _PUBSUB_COMMANDS = (
@asyncio.coroutine
-def create_connection(address, *, db=None, password=None,
+def create_connection(address, *, db=None, password=None, ssl=None,
encoding=None, loop=None):
"""Creates redis connection.
@@ -45,6 +48,9 @@ def create_connection(address, *, db=None, password=None,
* when address is a str it represents unix domain socket path.
(no other address formats supported)
+ SSL argument is passed through to asyncio.create_connection.
+ By default SSL/TLS is not used.
+
Encoding argument can be used to decode byte-replies to strings.
By default no decoding is done.
@@ -58,14 +64,14 @@ def create_connection(address, *, db=None, password=None,
host, port = address
logger.debug("Creating tcp connection to %r", address)
reader, writer = yield from asyncio.open_connection(
- host, port, loop=loop)
+ host, port, ssl=ssl, loop=loop)
sock = writer.transport.get_extra_info('socket')
if sock is not None:
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
else:
logger.debug("Creating unix connection to %r", address)
reader, writer = yield from asyncio.open_unix_connection(
- address, loop=loop)
+ address, ssl=ssl, loop=loop)
conn = RedisConnection(reader, writer, encoding=encoding, loop=loop)
try:
@@ -146,13 +152,8 @@ class RedisConnection:
def _process_data(self, obj):
"""Processes command results."""
waiter, encoding, cb = self._waiters.popleft()
- if waiter.done():
- logger.debug("Waiter future is already done %r", waiter)
- assert waiter.cancelled(), (
- "waiting future is in wrong state", waiter, obj)
- return
if isinstance(obj, RedisError):
- waiter.set_exception(obj)
+ _set_exception(waiter, obj)
if self._in_transaction is not None:
self._transaction_error = obj
else:
@@ -160,25 +161,24 @@ class RedisConnection:
try:
obj = decode(obj, encoding)
except Exception as exc:
- waiter.set_exception(exc)
+ _set_exception(waiter, exc)
return
if cb is not None:
try:
obj = cb(obj)
except Exception as exc:
- waiter.set_exception(exc)
+ _set_exception(waiter, exc)
return
- waiter.set_result(obj)
+ _set_result(waiter, obj)
if self._in_transaction is not None:
self._in_transaction.append((encoding, cb))
def _process_pubsub(self, obj, *, _process_waiters=True):
"""Processes pubsub messages."""
kind, *pattern, chan, data = obj
- if _process_waiters and self._in_pubsub and self._waiters:
- self._process_data(obj)
-
if kind in (b'subscribe', b'unsubscribe'):
+ if _process_waiters and self._in_pubsub and self._waiters:
+ self._process_data(obj)
if kind == b'subscribe' and chan not in self._pubsub_channels:
self._pubsub_channels[chan] = Channel(chan, is_pattern=False,
loop=self._loop)
@@ -188,6 +188,8 @@ class RedisConnection:
ch.close()
self._in_pubsub = data
elif kind in (b'psubscribe', b'punsubscribe'):
+ if _process_waiters and self._in_pubsub and self._waiters:
+ self._process_data(obj)
if kind == b'psubscribe' and chan not in self._pubsub_patterns:
self._pubsub_patterns[chan] = Channel(chan, is_pattern=True,
loop=self._loop)
@@ -253,6 +255,8 @@ class RedisConnection:
command = command.upper().strip()
assert command in _PUBSUB_COMMANDS, (
"Pub/Sub command expected", command)
+ if self._reader is None or self._reader.at_eof():
+ raise ConnectionClosedError("Connection closed or corrupted")
if None in set(channels):
raise TypeError("args must not contain None")
if not len(channels):
@@ -330,24 +334,27 @@ class RedisConnection:
return wait_ok(fut)
def _set_db(self, ok, args):
- assert ok in {b'OK', 'OK'}, ok
+ assert ok in {b'OK', 'OK'}, ("Unexpected result of SELECT", ok)
self._db = args[0]
return ok
def _start_transaction(self, ok):
- assert self._in_transaction is None
+ assert self._in_transaction is None, (
+ "Connection is already in transaction", self._in_transaction)
self._in_transaction = deque()
self._transaction_error = None
return ok
def _end_transaction(self, obj, discard):
- assert self._in_transaction is not None
+ assert self._in_transaction is not None, (
+ "Connection is not in transaction", obj)
self._transaction_error = None
recall, self._in_transaction = self._in_transaction, None
recall.popleft() # ignore first (its _start_transaction)
if discard:
return obj
- assert len(obj) == len(recall), (obj, recall)
+ assert len(obj) == len(recall), (
+ "Wrong number of result items in mutli-exec", obj, recall)
res = []
for o, (encoding, cb) in zip(obj, recall):
if not isinstance(o, RedisError):
diff --git a/aioredis/pool.py b/aioredis/pool.py
index 75315f0..59ec14f 100644
--- a/aioredis/pool.py
+++ b/aioredis/pool.py
@@ -11,7 +11,7 @@ PY_35 = sys.version_info >= (3, 5)
@asyncio.coroutine
-def create_pool(address, *, db=0, password=None, encoding=None,
+def create_pool(address, *, db=0, password=None, ssl=None, encoding=None,
minsize=10, maxsize=10, commands_factory=Redis, loop=None):
"""Creates Redis Pool.
@@ -27,7 +27,7 @@ def create_pool(address, *, db=0, password=None, encoding=None,
pool = RedisPool(address, db, password, encoding,
minsize=minsize, maxsize=maxsize,
commands_factory=commands_factory,
- loop=loop)
+ ssl=ssl, loop=loop)
yield from pool._fill_free(override_min=False)
return pool
@@ -37,12 +37,13 @@ class RedisPool:
"""
def __init__(self, address, db=0, password=None, encoding=None,
- *, minsize, maxsize, commands_factory, loop=None):
+ *, minsize, maxsize, commands_factory, ssl=None, loop=None):
if loop is None:
loop = asyncio.get_event_loop()
self._address = address
self._db = db
self._password = password
+ self._ssl = ssl
self._encoding = encoding
self._minsize = minsize
self._factory = commands_factory
@@ -193,6 +194,7 @@ class RedisPool:
return create_redis(self._address,
db=self._db,
password=self._password,
+ ssl=self._ssl,
encoding=self._encoding,
commands_factory=self._factory,
loop=self._loop)
diff --git a/aioredis/util.py b/aioredis/util.py
index 308affc..0de1407 100644
--- a/aioredis/util.py
+++ b/aioredis/util.py
@@ -3,11 +3,13 @@ import json
import sys
from .errors import ChannelClosedError
+from .log import logger
PY_35 = sys.version_info >= (3, 5)
_NOTSET = object()
+
# NOTE: never put here anything else;
# just this basic types
_converters = {
@@ -283,6 +285,24 @@ if PY_35:
return msg
+def _set_result(fut, result):
+ if fut.done():
+ logger.debug("Waiter future is already done %r", fut)
+ assert fut.cancelled(), (
+ "waiting future is in wrong state", fut, result)
+ else:
+ fut.set_result(result)
+
+
+def _set_exception(fut, exception):
+ if fut.done():
+ logger.debug("Waiter future is already done %r", fut)
+ assert fut.cancelled(), (
+ "waiting future is in wrong state", fut, exception)
+ else:
+ fut.set_exception(exception)
+
+
if hasattr(asyncio, 'ensure_future'):
async_task = asyncio.ensure_future
else:
diff --git a/setup.cfg b/setup.cfg
index 6bc2ff3..861a9f5 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,5 +1,5 @@
[egg_info]
-tag_date = 0
tag_build =
+tag_date = 0
tag_svn_revision = 0
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/aioredis.git
More information about the Python-modules-commits
mailing list