From c987ba43f2f19ca822ca6f8a39104a35c7bf61c1 Mon Sep 17 00:00:00 2001 From: Michal Szczepanski Date: Mon, 17 Aug 2020 12:11:31 +0200 Subject: [PATCH] Add Queue, Dictionary, Set, Counter --- .gitignore | 4 + example/structures.py | 63 ++++++++++++++ redistructures.py | 198 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 265 insertions(+) create mode 100644 .gitignore create mode 100644 example/structures.py create mode 100644 redistructures.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1c320fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +# python +__pycache__ +# ide +*.iml diff --git a/example/structures.py b/example/structures.py new file mode 100644 index 0000000..5229719 --- /dev/null +++ b/example/structures.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +from redistructures import Struct + +def test_dict(): + print('-'*10, 'Dictionary', '-'*10) + d = Struct.dictionary() + d['x'] = 'y' + d['w'] = 'v' + print(d['x']) + print('x' in d) + print('q' in d) + for k in d.keys(): + print(k) + for v in d.values(): + print(v) + for k, v in d.items(): + print(k, v) + print('-'*50) + +def test_set(): + print('-'*10, 'Set', '-'*10) + s1 = Struct.set('s1') + s2 = Struct.set('s2') + s1.add(1) + s1.add(2) + s1.add(3) + s2.add(3) + s2.add(4) + s2.add(5) + print(s1.pyset()) + print(s2.pyset()) + print(s1 - s2, s1 + s2, len(s1), len(s2)) + for v in s2: + print(v) + print('-'*50) + +def test_counter(): + print('-'*10, 'Counter', '-'*10) + c = Struct.counter() + print(c.incr(padding=10)) + print(c.incr(padding=10)) + print(c.decr(padding=10)) + print(c.value()) + print('-'*50) + +def test_queue(): + print('-'*10, 'Queue', '-'*10) + q = Struct.queue() + q.add('x') + q.add('y') + q.add('z') + k = q.get() + while k: + print(k) + k = q.get(timeout=1) + print('-'*50) + +if __name__ == '__main__': + test_dict() + test_set() + test_counter() + test_queue() diff --git a/redistructures.py b/redistructures.py new file mode 100644 index 0000000..b3cdab9 --- /dev/null +++ b/redistructures.py @@ -0,0 +1,198 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +import redis + +class Connection: + REDIS = None + HOST = "localhost" + PORT = 6379 + DB = 0 + @classmethod + def get_connection(cls): + if not Connection.REDIS: + Connection.REDIS = redis.StrictRedis(host=Connection.HOST, + port=Connection.PORT, + db=Connection.DB) + return Connection.REDIS + +class Struct: + @staticmethod + def set_iterator(key="set"): + """Return redis set iterator based on provided key""" + return SetIterator(connection=Connection.get_connection(), key=key) + + @staticmethod + def set(key="set"): + """Return redis set based on provided key""" + return Set(connection=Connection.get_connection(), key=key) + + @staticmethod + def dictionary(key="dict"): + """Return redis key / value structure""" + return Dict(connection=Connection.get_connection(), key=key) + + @staticmethod + def queue(key="queue"): + """Return redis queue based on list lpush/brpop""" + return Queue(connection=Connection.get_connection(), key=key) + + @staticmethod + def counter(key="counter"): + return Counter(connection=Connection.get_connection(), key=key) + + +class Queue: + def __init__(self, connection, key="queue"): + self._key = key + self._conn = connection + + @property + def key(self): + return self._key + + def get(self, timeout=0): + """Blocking atomic get from redis queue""" + return self._conn.brpop(self._key, timeout=timeout) + + def add(self, value): + """Add to redis queue""" + return self._conn.lpush(self._key, value) + + +class Dict: + """Dictionary on top of redis""" + def __init__(self, connection, key="dict"): + self._conn = connection + self.key = key + + def exists(self, key): + if self._conn.exists(f"{self.key}:{key}"): + return True + + def __setitem__(self, key, value): + self._conn.set(f"{self.key}:{key}", value) + return value + + def __getitem__(self, key): + return self._conn.get(f"{self.key}:{key}") + + def __contains__(self, key): + return self._conn.get(f"{self.key}:{key}") + + def keys(self, wildcard="*"): + return self._conn.scan_iter(f"{self.key}:{wildcard}") + + def values(self, wildcard="*"): + iter = self._conn.scan_iter(f"{self.key}:{wildcard}") + for key in iter: + yield self._conn.get(key) + + def items(self, wildcard="*"): + iter = self._conn.scan_iter(f"{self.key}:{wildcard}") + for key in iter: + yield key, self._conn.get(key) + + def set(self, key, value): + self._conn.set(f"{self.key}:{key}", value) + return value + + def get(self, key): + return self._conn.get(f"{self.key}:{key}") + + def getcheck(self, key): + if self.exists(key): + return self.get(f"{self.key}:{key}") + return False + + +class SetIterator: + """Set iterator on top of redis""" + def __init__(self, connection, key="set"): + self._conn = connection + self._key = key + self._iter = self._conn.sscan_iter(self._key) + + def __next__(self): + """Iterator next value""" + return next(self._iter) + + def next(self): + """Iterator next value""" + return next(self._iter) + + def __iter__(self): + return self + + +class Set: + """Set on top of redis""" + def __init__(self, connection, key="set"): + self._conn = connection + self._key = key + self._iter = None + + @property + def key(self): + return self._key + + def add(self, value): + """Add value to set""" + self._conn.sadd(self._key, value) + + def remove(self, value): + """Remove value from set""" + self._conn.srem(self._key, value) + + def pyset(self): + return self._conn.smembers(self._key) + + def __len__(self): + """Set length""" + return self._conn.scard(self._key) + + def __contains__(self, value): + """Check if value is in set""" + return self._conn.sismember(self._key, value) + + def __iter__(self): + """Return set iterator @see SetIterator""" + return SetIterator(connection=self._conn, key=self._key) + + def __add__(self, set2): + """Add two set together based on provided set key""" + return self._conn.sunion(self._key, set2.key) + + def __sub__(self, set2): + """Substracts two sets based on provided set key""" + return self._conn.sdiff(self._key, set2.key) + + def __repr__(self): + return "{}".format(self.pyset()) + + +class Counter: + def __init__(self, connection, key="counter"): + self._conn = connection + self._key = key + if self._conn.exists(key): + self._count = int(self._conn.get(key)) + else: + self._count = int(self._conn.set(key, 0)) + + @property + def key(self): + return self._key + + def value(self, padding=0): + return ("{0:0"+str(padding)+"d}").format(self._count) + + def __repr__(self): + return str(self._count) + + def incr(self, padding=0): + self._count = self._conn.incr(self._key) + return ("{0:0"+str(padding)+"d}").format(self._count) + + def decr(self, padding=0): + self._count = self._conn.decr(self._key) + return ("{0:0"+str(padding)+"d}").format(self._count)