Heiss/py-datatype-redis

View on GitHub
datatype_redis/types/base.py

Summary

Maintainability
A
2 hrs
Test Coverage
import uuid
from ..client import default_client, transaction, get_prefix
from .operator import op_left, op_right, inplace
import operator
from functools import wraps
from redis.exceptions import ResponseError


def ValueDecorator(fn):
    @wraps(fn)
    def wrapper(self, *args):
        try:
            arguments = list(args)
            arguments[0] = arguments[0].value
            args = tuple(arguments)
        except AttributeError:
            pass

        return fn(self, *args)

    return wrapper


class Base(object):
    def __init__(
        self,
        initial=None,
        key=None,
        serializer=None,
        client=None,
        namespace=None,
        prefix_format = "{}/{{}}",
        **kwargs
    ):
        """Base type that all others inherit. Contains the basic comparison
        operators as well as the dispatch for proxying to methods on the
        Redis client.

        Args:
            initial (object, optional): Set the value to this initial value. Defaults to None.
            key (string, optional): Set the key of this object to this value. Defaults to None.
            serializer (object, optional): Use this serializer. Needs `loads` and `dumps` method. Defaults to None.
            client (object, optional): Use this (redis) client. Defaults to None.
            namespace (string, optional): Use this namespace. Overwrites prefix from configure. Defaults to None.

        Raises:
            ValueError: Raises, when the serializer is not valid.
        """

        try:
            self.client = client(kwargs)
        except TypeError:
            self.client = client
        except Exception:
            self.client = None

        if serializer is not None:
            if not hasattr(serializer, "loads") or not hasattr(serializer, "dumps"):
                raise ValueError(
                    "serializer does not have loads or dumps method.")

            self.loads = serializer.loads
            self.dumps = serializer.dumps
        else:
            from msgpack import dumps, loads

            self.loads = loads
            self.dumps = dumps

        self.key = key or str(uuid.uuid4())

        self._prefix = namespace or get_prefix
        self.prefixer =  prefix_format.format(self.prefix).format

        if initial is not None:
            if key is None:
                self.value = initial
            else:
                # Ensure previous value removed if key and initial
                # value provided.
                with transaction():
                    self.client.delete(self.prefixer(self.key))
                    self.value = initial

    __eq__ = op_left(operator.eq)
    __lt__ = op_left(operator.lt)
    __le__ = op_left(operator.le)
    __gt__ = op_left(operator.gt)
    __ge__ = op_left(operator.ge)

    @property
    def prefix(self):
        if isinstance(self._prefix, str):
            return self._prefix
        return self._prefix()

    @property
    def value(self):
        raise NotImplementedError()

    @value.setter
    def value(self, value):
        raise NotImplementedError()

    @property
    def client(self):
        return self._client or default_client()

    @client.setter
    def client(self, value):
        self._client = value

    def __repr__(self):
        bits = (self.__class__.__name__, repr(self.value), self.key)
        return "%s(%s, '%s')" % bits


    def clear(self):
        self.client.delete(self.prefixer(self.key))

    def rename(self, new_redis_key):
        """Moves the value to a new key. 

        If the key is already in use, it returns False

        Args:
            new_redis_key (string): The new key for this object.

        Returns:
            bool: True, if rename process was a success, otherwise False
        """
        if not self.client.exists(self.prefixer(self.key), new_redis_key):
            self.client.rename(self.prefixer(self.key), new_redis_key)
            self.key = new_redis_key
            return True

        return False

    def get_redis_key(self):
        """Returns the redis key without prefix.

        This key is not equal to a dict key and cannot be used to interact with redis!

        Returns:
            string: The shortened redis key
        """
        return self.key

    def get_redis_key_full(self):
        """Returns the full redis key with prefix.

        This key can be used to interact with redis.

        Returns:
            string: The full redis key
        """
        return self.prefixer(self.key)