예제 #1
0
def redis():
    rd = Redis()
    keys = set(rd.keys())
    yield rd
    to_del = [k for k in rd.keys() if k not in keys]
    if to_del:
        rd.delete(*to_del)
class RedisClient:

    conn = ""
    CHANNEL = "key-update"
    pubsub = ""

    def __init__(self, db_path):
        os.makedirs(os.path.dirname(db_path), exist_ok=True)

        try:
            if not self.conn:
                self.conn = Redis(db_path)
        except:
            raise Exception("Unable to create connection with Redis server")

    def get_key(self, key):
        return self.conn.get(key)

    def set_key(self, key, value, publish_update=False):
        if publish_update:
            self.publish_update(message=json.dumps({key: value}))

        return self.conn.set(key, value)

    def remove_key(self, key):
        return self.conn.delete(key)

    def publish_update(self, channel=CHANNEL, message=""):
        self.conn.pubsub()
        self.conn.publish(channel, message)

    def get_update(self, channel=CHANNEL):
        if not self.pubsub:
            self.pubsub = self.conn.pubsub()
            self.pubsub.subscribe(channel)

        return self.pubsub.get_message(channel)
예제 #3
0
class HotQueue(object):
    """Simple FIFO message queue stored in a Redis list.

    Parameters
    ----------

    name : str
        name of the queue

    max_queue_length : int
        Maximum length the queue can grow to (default is None allows the queue
        to grow without any limits.

    serializer : class, module, optional
        the class or module to serialize msgs with, must have
        methods or functions named ``dumps`` and ``loads``,
        `pickle <http://docs.python.org/library/pickle.html>`_ is the default,
        use ``None`` to store messages in plain text (suitable for strings,
        integers, etc)

    redis : redis.Redis, redislite.Redis, optional
        redis connection object, defaults to redislite.Redis with fallback to
        redis.Redis.

    **kwargs
        Additional kwargs to pass to :class:`redislite.Redis`, most commonly
        :attr:`dbfilename`.

    Examples
    --------

    >>> from hotqueue import HotQueue
    >>> queue = HotQueue("myqueue", dbfilename="queue.rdb")

    """

    def __init__(
            self, name, serializer=pickle, redis=None, max_queue_length=None,
            **kwargs
    ):
        self.name = name
        self.serializer = serializer
        self.max_queue_length = max_queue_length
        if redis:
            self.__redis = redis
        else:
            self.__redis = Redis(**kwargs)

    def __len__(self):
        return self.__redis.llen(self.key)

    @property
    def key(self):
        """
        Key in Redis to store the queue

        Returns
        -------
        str
            The name of the key containing the queue in redis.
        """
        return key_for_name(self.name)

    def clear(self):
        """
        Clear the queue of all messages, by deleting the Redis key.
        """
        self.__redis.delete(self.key)

    def consume(self, **kwargs):
        """
        A blocking generator that yields whenever a message is waiting in the
        queue.

        Parameters
        ----------

        **kwargs
            any arguments that :meth:`~hotqueue.HotQueue.get` can
            accept (:attr:`block` will default to ``True`` if not given)

        Yields
        ------
        object
            The deserialized object from the queue.

        Examples
        --------

        >>> queue = HotQueue("example")
        >>> for msg in queue.consume(timeout=1):
        ...     print(msg)
        my message
        another message

        """
        kwargs.setdefault('block', True)
        try:
            while True:
                msg = self.get(**kwargs)
                if msg is None:
                    break
                yield msg
        except KeyboardInterrupt:  # pragma: no cover
            print()
            return

    def get(self, block=False, timeout=None):
        """
        Get a message from the queue.

        Parameters
        ----------

        block : bool
            whether or not to wait until a msg is available in
            the queue before returning; ``False`` by default

        timeout : int
            When using :attr:`block`, if no msg is available
            for :attr:`timeout` in seconds, give up and return

        Returns
        -------
        object
            The deserialized object from the queue.

        Examples
        --------

        >>> queue.get()
        'my message'
        >>> queue.get()
        'another message'

        """
        if block:
            if timeout is None:
                timeout = 0
            msg = self.__redis.blpop(self.key, timeout=timeout)
            if msg is not None:
                msg = msg[1]
        else:
            msg = self.__redis.lpop(self.key)
        if msg is not None and self.serializer is not None:
            msg = self.serializer.loads(msg)

        if isinstance(msg, bytes):
            msg = msg.decode()

        return msg

    def put(self, *msgs):
        """Put one or more messages onto the queue. Example:

        >>> queue.put("my message")
        >>> queue.put("another message")

        To put messages onto the queue in bulk, which can be significantly
        faster if you have a large number of messages:

        >>> queue.put("my message", "another message", "third message")
        """
        if self.serializer is not None:
            msgs = [self.serializer.dumps(m) for m in msgs]
        self.__redis.rpush(self.key, *msgs)
        if self.max_queue_length:
            self.__redis.ltrim(self.key, 0, int(self.max_queue_length) - 1)

    def worker(self, *args, **kwargs):
        """Decorator for using a function as a queue worker. Example:

        >>> @queue.worker(timeout=1)
        ... def printer(msg):
        ...     print(msg)
        >>> printer()
        my message
        another message

        You can also use it without passing any keyword arguments:

        >>> @queue.worker
        ... def printer(msg):
        ...     print(msg)
        >>> printer()
        my message
        another message

        :param kwargs: any arguments that :meth:`~hotqueue.HotQueue.get` can
            accept (:attr:`block` will default to ``True`` if not given)
        """
        def decorator(worker):
            """
            Worker decorator
            :param worker:
            :return:
            """
            @wraps(worker)
            def wrapper(*args):
                """
                Inner wrapper
                :param args:
                :return:
                """
                for msg in self.consume(**kwargs):
                    worker(*args + (msg,))
            return wrapper
        if args:
            return decorator(*args)
        return decorator
예제 #4
0
class RedisStore(DataStore):
    """Implementation of Redis datastore"""
    def __init__(self):
        super().__init__()
        if config.settings.stackl_redis_type == "fake":
            logger.info("Using fake client")

            self.redis = Redis()
        else:
            self.redis = redis.Redis(
                host=config.settings.stackl_redis_host,
                port=config.settings.stackl_redis_port,
                password=config.settings.stackl_redis_password,
                db=0)

    def get(self, **keys):
        """Gets a document from a redis instance"""
        document_key = keys.get("category") + '/' + keys.get(
            "type") + '/' + keys.get("name")
        logger.debug(f"[RedisStore] get on key '{document_key}'")

        redis_value = self.redis.get(document_key)
        if redis_value is None:
            response = self._create_store_response(
                status_code=StatusCode.NOT_FOUND, content={})
        else:
            content = json.loads(self.redis.get(document_key))
            response = self._create_store_response(status_code=StatusCode.OK,
                                                   content=content)
        logger.debug(f"[RedisStore] StoreResponse for get: {response}")
        return response

    def get_all(self, category, document_type, wildcard_prefix=""):
        """Gets all documents of a type from a Redis"""
        document_key = f"{category}/{document_type}/{wildcard_prefix}*"
        logger.debug(
            f"[RedisStore] get_all in '{document_key}' for type '{document_type}'"
        )
        content = []
        for key in self.redis.scan_iter(document_key):
            content.append(json.loads(self.redis.get(key)))
        response = self._create_store_response(status_code=StatusCode.OK,
                                               content=content)
        logger.debug(f"[RedisStore] StoreResponse for get: {response}")
        return response

    def get_history(self, category, document_type, name):
        """Gets the snapshots of document from Redis"""
        document_key = category + '/' + document_type + '/' + name
        logger.debug(
            f"[RedisStore] get_history in '{document_key}' for type '{document_type}'"
        )
        content = []
        for key in self.redis.scan_iter(document_key):
            content.append(json.loads(self.redis.get(key)))
        response = self._create_store_response(status_code=StatusCode.OK,
                                               content=content)
        logger.debug(f"[RedisStore] StoreResponse for get: {response}")
        return response

    def put(self, file):
        """Puts a document in Redis"""
        document_key = file.get("category") + '/' + file.get(
            "type") + '/' + file["name"]
        logger.debug(f"[RedisStore] put on '{document_key}' with file {file}")
        self.redis.set(document_key, json.dumps(file))
        response = self._create_store_response(
            status_code=StatusCode.CREATED,
            content=json.loads(self.redis.get(document_key)))
        logger.debug(f"[RedisStore] StoreResponse for put: {response}")
        return response

    def delete(self, **keys):
        """Deletes a document in Redis"""
        document_key = keys.get("category") + '/' + keys.get(
            "type") + '/' + keys.get("name")
        self.redis.delete(document_key)
        response = self._create_store_response(status_code=200, content={})
        logger.debug(f"[RedisStore] StoreResponse for delete: {response}")
        return response