class ResponseError(RedisError):
    pass class RedisError(Exception):
    pass class DataError(RedisError):
    pass def merge_result(res):
    """
    Merges all items in `res` into a list.

    This command is used when sending a command to multiple nodes
    and they result from each node should be merged into a single list.
    """
    if not isinstance(res, dict):
        raise ValueError('Value should be of dict type')

    result = set([])

    for _, v in res.items():
        for value in v:
            result.add(value)

    return list(result) class NodeFlag:
    BLOCKED = 'blocked'
    ALL_NODES = 'all-nodes'
    ALL_MASTERS = 'all-masters'
    RANDOM = 'random'
    SLOT_ID = 'slot-id' def first_key(res):
    """
    Returns the first result for the given command.

    If more then 1 result is returned then a `RedisClusterException` is raised.
    """
    if not isinstance(res, dict):
        raise ValueError('Value should be of dict type')

    if len(res.keys()) != 1:
        raise RedisClusterException("More then 1 result from command")

    return list(res.values())[0] def b(x):
    return x.encode('latin-1') if not isinstance(x, bytes) else x def dict_merge(*dicts):
    merged = {}
    for d in dicts:
        merged.update(d)
    return merged def int_or_none(response):
    if response is None:
        return None
    return int(response) def bool_ok(response):
    return nativestr(response) == 'OK' def string_keys_to_dict(key_string, callback):
    return dict.fromkeys(key_string.split(), callback) def list_keys_to_dict(key_list, callback):
    return dict.fromkeys(key_list, callback)
import datetime
import time as mod_time
from aredis.exceptions import (ResponseError,
                               RedisError,
                               DataError)
from aredis.utils import (merge_result,
                          NodeFlag,
                          first_key,
                          b, dict_merge,
                          int_or_none,
                          bool_ok,
                          string_keys_to_dict,
                          list_keys_to_dict)
        """
        params = [name, ttl, value]
        if replace:
            params.append('REPLACE')
        return await self.execute_command('RESTORE', *params)

    async def sort(self, name, start=None, num=None, by=None, get=None,
             desc=False, alpha=False, store=None, groups=False):
        """
        Sorts and returns a list, set or sorted set at ``name``.

        ``start`` and ``num`` are for paginating sorted data

        ``by`` allows using an external key to weight and sort the items.
            Use an "*" to indicate where in the key the item value is located

        ``get`` is for returning items from external keys rather than the
            sorted data itself.  Use an "*" to indicate where int he key
            the item value is located

        ``desc`` is for reversing the sort

        ``alpha`` is for sorting lexicographically rather than numerically

        ``store`` is for storing the result of the sort into
            the key ``store``

        ``groups`` if set to True and if ``get`` contains at least two
            elements, sort will return a list of tuples, each containing the
            values fetched from the arguments to ``get``.

        """
        if (start is not None and num is None) or \
                (num is not None and start is None):
            raise RedisError("``start`` and ``num`` must both be specified")

        pieces = [name]
        if by is not None:
            pieces.append(b('BY'))
            pieces.append(by)
        if start is not None and num is not None:
            pieces.append(b('LIMIT'))
            pieces.append(start)
            pieces.append(num)
        if get is not None:
            # If get is a string assume we want to get a single value.
            # Otherwise assume it's an interable and we want to get multiple
            # values. We can't just iterate blindly because strings are
            # iterable.
            if isinstance(get, str):
                pieces.append(b('GET'))
                pieces.append(get)
            else:
                for g in get:
                    pieces.append(b('GET'))
                    pieces.append(g)
        if desc:
            pieces.append(b('DESC'))
        if alpha:
            pieces.append(b('ALPHA'))
        if store is not None:
            pieces.append(b('STORE'))
            pieces.append(store)

        if groups:
            if not get or isinstance(get, str) or len(get) < 2:
                raise DataError('when using "groups" the "get" argument '
                                'must be specified and contain at least '
                                'two keys')

        options = {'groups': len(get) if groups else None}
        return await self.execute_command('SORT', *pieces, **options)

    async def touch(self, keys):
        """
        Alters the last access time of a key(s).
        A key is ignored if it does not exist.
        """
        return await self.execute_command('TOUCH', *keys)

    async def ttl(self, name):
        """Returns the number of seconds until the key ``name`` will expire"""
        return await self.execute_command('TTL', name)

    async def type(self, name):
        """Returns the type of key ``name``"""
        return await self.execute_command('TYPE', name)

    async def unlink(self, *keys):
        """Removes the specified keys in a different thread, not blocking"""
        return await self.execute_command('UNLINK', *keys)

    async def wait(self, num_replicas, timeout):
        """
        Redis synchronous replication
        That returns the number of replicas that processed the query when
        we finally have at least ``num_replicas``, or when the ``timeout`` was
        reached.
        """
        return await self.execute_command('WAIT', num_replicas, timeout)

    async def scan(self, cursor=0, match=None, count=None):
        """
        Incrementally return lists of key names. Also return a cursor
        indicating the scan position.

        ``match`` allows for filtering the keys by pattern

        ``count`` allows for hint the minimum number of returns
        """
        pieces = [cursor]
        if match is not None:
            pieces.extend([b('MATCH'), match])
        if count is not None:
            pieces.extend([b('COUNT'), count])
        return await self.execute_command('SCAN', *pieces)


class ClusterKeysCommandMixin(KeysCommandMixin):


    NODES_FLAGS = dict_merge(