Skip to content

Commit

Permalink
support aioredis hash functions
Browse files Browse the repository at this point in the history
  • Loading branch information
yunitto committed Oct 6, 2018
1 parent ab01908 commit 6d0cee4
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 3 deletions.
4 changes: 2 additions & 2 deletions ring/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
dict, shelve, disk, memcache, redis, redis_hash)
try:
import asyncio
from ring.func.asyncio import aiomcache, aioredis
from ring.func.asyncio import aiomcache, aioredis, aioredis_hash
except ImportError:
pass
else:
Expand All @@ -22,4 +22,4 @@

__all__ = (
'dict', 'shelve', 'memcache', 'redis', 'redis_hash', 'disk',
'aiomcache', 'aioredis')
'aiomcache', 'aioredis', 'aioredis_hash')
4 changes: 3 additions & 1 deletion ring/func/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
redis = asyncio.create_asyncio_factory_proxy(
(sync.redis_py, asyncio.aioredis),
support_asyncio=True)
from .sync import redis_py_hash as redis_hash
redis_hash = asyncio.create_asyncio_factory_proxy(
(sync.redis_py_hash, asyncio.aioredis_hash),
support_asyncio=True)
else:
from .sync import dict, shelve, diskcache as disk, memcache, redis_py as redis, redis_py_hash as redis_hash
102 changes: 102 additions & 0 deletions ring/func/asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,53 @@ def set_many_values(self, keys, values, expire):
backend.expire(key, expire) for key in keys)))


class AioredisHashStorage(AioredisStorage):
"""Storage implementation for :class:`aioredis.Redis`."""

def __init__(self, rope, backend):
storage_backend = backend[0]
self.hash_key = backend[1]
super(AioredisHashStorage, self).__init__(rope, storage_backend)

@asyncio.coroutine
def get_value(self, key):
backend = yield from self._get_backend()
value = yield from backend.hget(self.hash_key, key)
if value is None:
raise fbase.NotFound
return value

@asyncio.coroutine
def set_value(self, key, value, expire):
backend = yield from self._get_backend()
result = yield from backend.hset(self.hash_key, key, value)
return result

@asyncio.coroutine
def delete_value(self, key):
backend = yield from self._get_backend()
result = yield from backend.hdel(self.hash_key, key)
return result

@asyncio.coroutine
def has_value(self, key):
backend = yield from self._get_backend()
result = yield from backend.hexists(self.hash_key, key)
return bool(result)

@asyncio.coroutine
def get_many_values(self, keys):
backend = yield from self._get_backend()
values = yield from backend.hmget(self.hash_key, *keys)
return [v if v is not None else fbase.NotFound for v in values]

@asyncio.coroutine
def set_many_values(self, keys, values, expire):
params = itertools.chain.from_iterable(zip(keys, values))
backend = yield from self._get_backend()
yield from backend.hmset(self.hash_key, *params)


def dict(
obj, key_prefix=None, expire=None, coder=None, ignorable_keys=None,
user_interface=CacheUserInterface, storage_class=None,
Expand Down Expand Up @@ -535,3 +582,58 @@ def aioredis(
miss_value=None, expire_default=expire, coder=coder,
ignorable_keys=ignorable_keys,
**kwargs)


def aioredis_hash(
redis, hash_key=None, key_prefix=None, coder=None, ignorable_keys=None,
user_interface=(CacheUserInterface, BulkInterfaceMixin),
storage_class=AioredisHashStorage,
**kwargs):
"""Redis interface for :mod:`asyncio`.
Expected client package is aioredis_.
This implements HASH commands in aioredis.
aioredis expect `Redis` client or dev package is installed on your
machine. If you are new to Memcached, check how to install it and the
python package on your platform.
Note that aioredis>=1.0.0 only supported.
.. _Redis: http://redis.io/
.. _aioredis: https://pypi.org/project/aioredis/
:param Union[aioredis.Redis,Callable[...aioredis.Redis]] client: aioredis
interface object. See :func:`aioredis.create_redis` or
:func:`aioredis.create_redis_pool`. For convenience, a coroutine
returning one of these objects also is proper. It means next 2
examples working almost same:
>>> redis = await aioredis.create_redis(('127.0.0.1', 6379))
>>> @ring.aioredis_hash(redis, ...)
>>> async def by_object(...):
>>> ...
>>> redis_coroutine = aioredis.create_redis(('127.0.0.1', 6379))
>>> @ring.aioredis_hash(redis_coroutine, ...)
>>> async def by_coroutine(...):
>>> ...
:see: :func:`ring.func.asyncio.CacheUserInterface` for single access
sub-functions.
:see: :func:`ring.func.asyncio.BulkInterfaceMixin` for bulk access
sub-functions.
:see: :func:`ring.redis` for non-asyncio version.
"""
expire = None
if inspect.iscoroutine(redis):
redis = SingletonCoroutineProxy(redis)

return fbase.factory(
(redis, hash_key), key_prefix=key_prefix, on_manufactured=factory_doctor,
user_interface=user_interface, storage_class=storage_class,
miss_value=None, expire_default=expire, coder=coder,
ignorable_keys=ignorable_keys,
**kwargs)
60 changes: 60 additions & 0 deletions tests/_test_func_asyncio.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,66 @@ def f(a):
yield from f.touch_many()


@pytest.mark.asyncio
@asyncio.coroutine
def test_aioredis_hash(aioredis_pool):
client, _ = aioredis_pool

@ring.aioredis_hash(client, 'test-hashkey')
@asyncio.coroutine
def f(a):
return 't{}'.format(a).encode()

# delete previous test
yield from f.delete(1)
yield from f.delete(2)
yield from f.delete(3)

r = yield from f.get(1)
assert r is None

yield from f(1)
r = yield from f.has(1)
assert r is True
r = yield from f.get(1)
assert r == b't1'

r = yield from f.get_many(
(1,),
{'a': 2},
)
assert r == [b't1', None]

r = yield from f.update_many(
(1,),
{'a': 3},
)
assert r == [b't1', b't3']

yield from f.set_many((
(1,),
(2,),
), (
b'foo',
b'bar',
))

r = yield from f.get_many(
{'a': 1},
(2,),
(3,),
)
assert r == [b'foo', b'bar', b't3']
yield from f.delete(2)

r = yield from f.get_or_update_many(
(1,),
(2,),
(3,),
)
assert r == [b'foo', b't2', b't3']


@pytest.mark.asyncio
@asyncio.coroutine
def test_func_method(storage_dict):
Expand Down

0 comments on commit 6d0cee4

Please sign in to comment.