def eval(self, redis: StrictRedis, *keys, **kwargs): """Tries to call ``EVALSHA`` with the `hash` and then, if it fails, calls regular ``EVAL`` with. """ args = kwargs.pop('args', ()) # make sure number of keys passed matches keys required if len(keys) != self._num_keys: raise RedisScriptError( f"Key length mismatch. Expected {self._num_keys}. " f"Got {len(keys)} in {self._name}") if kwargs: raise TypeError( f"Unexpected keyword arguments {kwargs.keys()} in {self._name}." ) try: return redis.evalsha(self._script_digest, self._num_keys, *keys + args) except NoScriptError: return redis.eval(self._script_encoded, self._num_keys, *keys + args)
class Cache: def __init__(self): self.configured = False self.host = None self.port = None self.password = None self.prefix = None self._client = None def _connect(self): self._client = StrictRedis(self.host, port=self.port, password=self.password) def connect(self, host, port=6379, password=None, prefix=None): """ Only support db 0 """ self.configured = True self.host = host self.port = port self.password = password self.prefix = prefix if self.prefix is None: raise Exception('Prefix should provided') self._connect() def __getattribute__(self, attr, *args, **kwargs): try: return super().__getattribute__(attr) except AttributeError: if not self._client: raise Exception('Cache client not initialized') return getattr(self._session, attr) def make_key(self, key): return f'{self.prefix}:cache:{key}' @staticmethod def encode(value): if isinstance(value, bool) or not isinstance(value, int): return pickle.dumps(value) return value @staticmethod def decode(value): try: value = int(value) except (ValueError, TypeError): value = pickle.loads(value) return value def set(self, key, value, timeout=DEFAULT_TIMEOUT): key = self.make_key(key) value = self.encode(value) return bool(self._client.set(key, value, ex=timeout)) def get(self, key): key = self.make_key(key) value = self._client.get(key) if value is None: return value return self.decode(value) def _incr(self, key, delta=1): """Always check key exists """ # if key expired after exists check, then we get # key with wrong value and ttl -1. # use lua script for atomicity lua = """ local exists = redis.call('EXISTS', KEYS[1]) if (exists == 1) then return redis.call('INCRBY', KEYS[1], ARGV[1]) else return false end """ value = self._client.eval(lua, 1, key, delta) if value is None: raise ValueError("Key '%s' not found" % key) return value def incr(self, key, delta=1): key = self.make_key(key) return self._incr(key, delta=delta) def decr(self, key, delta=1): key = self.make_key(key) return self._incr(key, delta=-delta)