From 3b70503a9db0f33a38b87bd281914777a277ab32 Mon Sep 17 00:00:00 2001 From: Daan Debie Date: Tue, 12 Sep 2017 21:39:00 +0200 Subject: [PATCH] Add Redis storage support --- machine/storage/backends/redis.py | 37 ++++++++++++++++++++++++++++ requirements-dev.txt | 3 ++- setup.py | 7 ++++-- tests/test_redis_storage.py | 40 +++++++++++++++++++++++++++++++ 4 files changed, 84 insertions(+), 3 deletions(-) create mode 100644 machine/storage/backends/redis.py create mode 100644 tests/test_redis_storage.py diff --git a/machine/storage/backends/redis.py b/machine/storage/backends/redis.py new file mode 100644 index 00000000..a61d2f36 --- /dev/null +++ b/machine/storage/backends/redis.py @@ -0,0 +1,37 @@ +from urllib.parse import urlparse +from redis import StrictRedis + +from machine.storage.backends.base import MachineBaseStorage + + +class RedisStorage(MachineBaseStorage): + def __init__(self, settings): + super().__init__(settings) + self._key_prefix = settings.get('REDIS_KEY_PREFIX', 'SM') + url = urlparse(settings['REDIS_URL']) + if hasattr(url, "path"): + db = url.path[1:] + else: + db = 0 + max_connections = settings.get('REDIS_MAX_CONNECTIONS', None) + self._redis = StrictRedis(host=url.hostname, port=url.port, db=db, + password=url.password, max_connections=max_connections) + + def _prefix(self, key): + return "{}:{}".format(self._key_prefix, key) + + def has(self, key): + return self._redis.exists(self._prefix(key)) + + def get(self, key): + return self._redis.get(self._prefix(key)) + + def set(self, key, value, expires=None): + self._redis.set(self._prefix(key), value, expires) + + def delete(self, key): + self._redis.delete(self._prefix(key)) + + def size(self): + info = self._redis.info('memory') + return info['used_memory'] diff --git a/requirements-dev.txt b/requirements-dev.txt index 6b32d4fb..52187c2c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -10,4 +10,5 @@ twine~=1.5 coverage~=4.4 pytest-cov~=2.5 Sphinx~=1.6 -sphinx-autobuild~=0.7 \ No newline at end of file +sphinx-autobuild~=0.7 +redis~=2.10 \ No newline at end of file diff --git a/setup.py b/setup.py index eb5617ac..fc29b69c 100644 --- a/setup.py +++ b/setup.py @@ -76,6 +76,11 @@ def run(self): author_email=about["__email__"], setup_requires=['pytest-runner'], tests_require=['pytest', 'pytest-cov', 'coverage'], + install_requires=dependencies, + python_requires='~=3.3', + extras_require={ + 'redis': ['redis', 'hiredis'] + }, classifiers=[ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", @@ -92,8 +97,6 @@ def run(self): "Topic :: Office/Business" ], keywords='slack bot framework ai', - install_requires=dependencies, - python_requires='~=3.3', entry_points={ 'console_scripts': [ 'slack-machine = machine.bin.run:main', diff --git a/tests/test_redis_storage.py b/tests/test_redis_storage.py new file mode 100644 index 00000000..25bd2174 --- /dev/null +++ b/tests/test_redis_storage.py @@ -0,0 +1,40 @@ +from unittest.mock import MagicMock + +import pytest +from redis import StrictRedis + +from machine.storage.backends.redis import RedisStorage + +@pytest.fixture +def redis_client(): + return MagicMock(spec=StrictRedis) + +@pytest.fixture +def redis_storage(mocker, redis_client): + mocker.patch('machine.storage.backends.redis.StrictRedis', autospec=True) + settings = {'REDIS_URL': 'redis://nohost:1234'} + storage = RedisStorage(settings) + storage._redis = redis_client + return storage + +def test_set(redis_storage, redis_client): + redis_storage.set('key1', 'value1') + redis_client.set.assert_called_with('SM:key1', 'value1', None) + redis_storage.set('key2', 'value2', 42) + redis_client.set.assert_called_with('SM:key2', 'value2', 42) + +def test_get(redis_storage, redis_client): + redis_storage.get('key1') + redis_client.get.assert_called_with('SM:key1') + +def test_has(redis_storage, redis_client): + redis_storage.has('key1') + redis_client.exists.assert_called_with('SM:key1') + +def test_delete(redis_storage, redis_client): + redis_storage.delete('key1') + redis_client.delete.assert_called_with('SM:key1') + +def test_size(redis_storage, redis_client): + redis_storage.size() + redis_client.info.assert_called_with('memory')