Пример #1
0
    async def on_connect(self):
        self._parser.on_connect(self)

        # if a password is specified, authenticate
        if self.password:
            await self.send_command('AUTH', self.password)
            if nativestr(await self.read_response()) != 'OK':
                raise ConnectionError('Invalid Password')

        # if a database is specified, switch to it
        if self.db:
            await self.send_command('SELECT', self.db)
            if nativestr(await self.read_response()) != 'OK':
                raise ConnectionError('Invalid Database')
Пример #2
0
def parse_info(response):
    "Parse the result of Redis's INFO command into a Python dict"
    info = {}
    response = nativestr(response)

    def get_value(value):
        if ',' not in value or '=' not in value:
            try:
                if '.' in value:
                    return float(value)
                else:
                    return int(value)
            except ValueError:
                return value
        else:
            sub_dict = {}
            for item in value.split(','):
                k, v = item.rsplit('=', 1)
                sub_dict[k] = get_value(v)
            return sub_dict

    for line in response.splitlines():
        if line and not line.startswith('#'):
            if line.find(':') != -1:
                key, value = line.split(':', 1)
                info[key] = get_value(value)
            else:
                # if the line isn't splittable, append it to the "__raw__" key
                info.setdefault('__raw__', []).append(line)

    return info
Пример #3
0
def parse_role(response):
    role = nativestr(response[0])

    def _parse_master(response):
        offset, slaves = response[1:]
        res = {'role': role, 'offset': offset, 'slaves': []}
        for slave in slaves:
            host, port, offset = slave
            res['slaves'].append({
                'host': host,
                'port': int(port),
                'offset': int(offset)
            })
        return res

    def _parse_slave(response):
        host, port, status, offset = response[1:]
        return {
            'role': role,
            'host': host,
            'port': port,
            'status': status,
            'offset': offset
        }

    def _parse_sentinel(response):
        return {'role': role, 'masters': response[1:]}

    parser = {
        'master': _parse_master,
        'slave': _parse_slave,
        'sentinel': _parse_sentinel
    }[role]
    return parser(response)
Пример #4
0
 async def connect_to(self, address):
     self.host, self.port = address
     await super(SentinelManagedConnection, self).connect()
     if self.connection_pool.check_connection:
         await self.send_command('PING')
         if nativestr(await self.read_response()) != 'PONG':
             raise ConnectionError('PING failed')
Пример #5
0
def parse_georadius_generic(response, **options):
    if options['store'] or options['store_dist']:
        # `store` and `store_diff` cant be combined
        # with other command arguments.
        return response

    if type(response) != list:
        response_list = [response]
    else:
        response_list = response

    if not options['withdist'] and not options['withcoord']\
            and not options['withhash']:
        # just a bunch of places
        return [nativestr(r) for r in response_list]

    cast = {
        'withdist': float,
        'withcoord': lambda ll: (float(ll[0]), float(ll[1])),
        'withhash': int
    }

    # zip all output results with each casting functino to get
    # the properly native Python value.
    f = [nativestr]
    f += [cast[o] for o in ['withdist', 'withhash', 'withcoord'] if options[o]]
    return [
        list(map(lambda fv: fv[0](fv[1]), zip(f, r))) for r in response_list
    ]
Пример #6
0
    async def on_connect(self):
        self._parser.on_connect(self)

        # if a password is specified, authenticate
        if self.password:
            await self.send_command('AUTH', self.password)
            if nativestr(await self.read_response()) != 'OK':
                raise ConnectionError('Invalid Password')

        # if a database is specified, switch to it
        if self.db:
            await self.send_command('SELECT', self.db)
            if nativestr(await self.read_response()) != 'OK':
                raise ConnectionError('Invalid Database')

        if self.client_name is not None:
            await self.send_command('CLIENT SETNAME', self.client_name)
            if nativestr(await self.read_response()) != 'OK':
                raise ConnectionError('Failed to set client name: {}'.format(
                    self.client_name))
        self.last_active_at = time.time()
Пример #7
0
 async def on_connect(self):
     """
     Initialize the connection, authenticate and select a database and send READONLY if it is
     set during object initialization.
     """
     if self.db:
         warnings.warn('SELECT DB is not allowed in cluster mode')
         self.db = ''
     await super(ClusterConnection, self).on_connect()
     if self.readonly:
         await self.send_command('READONLY')
         if nativestr(await self.read_response()) != 'OK':
             raise ConnectionError('READONLY command failed')
Пример #8
0
    def handle_message(self, response, ignore_subscribe_messages=False):
        """
        Parses a pub/sub message. If the channel or pattern was subscribed to
        with a message handler, the handler is invoked instead of a parsed
        message being returned.
        """
        message_type = nativestr(response[0])
        if message_type == 'pmessage':
            message = {
                'type': message_type,
                'pattern': response[1],
                'channel': response[2],
                'data': response[3]
            }
        else:
            message = {
                'type': message_type,
                'pattern': None,
                'channel': response[1],
                'data': response[2]
            }

        # if this is an unsubscribe message, remove it from memory
        if message_type in self.UNSUBSCRIBE_MESSAGE_TYPES:
            subscribed_dict = None
            if message_type == 'punsubscribe':
                subscribed_dict = self.patterns
            else:
                subscribed_dict = self.channels
            try:
                del subscribed_dict[message['channel']]
            except KeyError:
                pass

        if message_type in self.PUBLISH_MESSAGE_TYPES:
            # if there's a message handler, invoke it
            handler = None
            if message_type == 'pmessage':
                handler = self.patterns.get(message['pattern'], None)
            else:
                handler = self.channels.get(message['channel'], None)
            if handler:
                handler(message)
                return None
        else:
            # this is a subscribe/unsubscribe message. ignore if we don't
            # want them
            if ignore_subscribe_messages or self.ignore_subscribe_messages:
                return None

        return message
Пример #9
0
class ConnectionCommandMixin:

    RESPONSE_CALLBACKS = {
        'AUTH': bool,
        'PING': lambda r: nativestr(r) == 'PONG',
        'SELECT': bool_ok,
    }

    async def echo(self, value):
        "Echo the string back from the server"
        return await self.execute_command('ECHO', value)

    async def ping(self):
        "Ping the Redis server"
        return await self.execute_command('PING')
Пример #10
0
def parse_debug_object(response):
    "Parse the results of Redis's DEBUG OBJECT command into a Python dict"
    # The 'type' of the object is the first item in the response, but isn't
    # prefixed with a name
    response = nativestr(response)
    response = 'type:' + response
    response = dict([kv.split(':') for kv in response.split()])

    # parse some expected int values from the string response
    # note: this cmd isn't spec'd so these may not appear in all redis versions
    int_fields = ('refcount', 'serializedlength', 'lru', 'lru_seconds_idle')
    for field in int_fields:
        if field in response:
            response[field] = int(response[field])

    return response
Пример #11
0
def parse_config_get(response, **options):
    response = [nativestr(i) if i is not None else None for i in response]
    return response and pairs_to_dict(response) or {}
Пример #12
0
class ServerCommandMixin:
    RESPONSE_CALLBACKS = dict_merge(
        string_keys_to_dict('BGREWRITEAOF BGSAVE', lambda r: True),
        string_keys_to_dict('FLUSHALL FLUSHDB SAVE '
                            'SHUTDOWN SLAVEOF', bool_ok), {
                                'ROLE': parse_role,
                                'SLOWLOG GET': parse_slowlog_get,
                                'SLOWLOG LEN': int,
                                'SLOWLOG RESET': bool_ok,
                                'CLIENT GETNAME': lambda r: r and nativestr(r),
                                'CLIENT KILL': bool_ok,
                                'CLIENT LIST': parse_client_list,
                                'CLIENT SETNAME': bool_ok,
                                'CLIENT PAUSE': bool_ok,
                                'CONFIG GET': parse_config_get,
                                'CONFIG RESETSTAT': bool_ok,
                                'CONFIG SET': bool_ok,
                                'DEBUG OBJECT': parse_debug_object,
                                'INFO': parse_info,
                                'LASTSAVE': timestamp_to_datetime,
                                'TIME': lambda x: (int(x[0]), int(x[1])),
                            })

    async def bgrewriteaof(self):
        "Tell the Redis server to rewrite the AOF file from data in memory."
        return await self.execute_command('BGREWRITEAOF')

    async def bgsave(self):
        """
        Tell the Redis server to save its data to disk.  Unlike save(),
        this method is asynchronous and returns immediately.
        """
        return await self.execute_command('BGSAVE')

    async def client_kill(self, address):
        "Disconnects the client at ``address`` (ip:port)"
        return await self.execute_command('CLIENT KILL', address)

    async def client_list(self):
        "Returns a list of currently connected clients"
        return await self.execute_command('CLIENT LIST')

    async def client_getname(self):
        "Returns the current connection name"
        return await self.execute_command('CLIENT GETNAME')

    async def client_setname(self, name):
        "Sets the current connection name"
        return await self.execute_command('CLIENT SETNAME', name)

    async def client_pause(self, timeout=0):
        """suspend all the Redis clients for the
        specified amount of time (in milliseconds)."""
        return await self.execute_command('CLIENT PAUSE', timeout)

    async def config_get(self, pattern="*"):
        "Return a dictionary of configuration based on the ``pattern``"
        return await self.execute_command('CONFIG GET', pattern)

    async def config_set(self, name, value):
        "Set config item ``name`` with ``value``"
        return await self.execute_command('CONFIG SET', name, value)

    async def config_resetstat(self):
        "Reset runtime statistics"
        return await self.execute_command('CONFIG RESETSTAT')

    async def config_rewrite(self):
        "Rewrite config file with the minimal change to reflect running config"
        return await self.execute_command('CONFIG REWRITE')

    async def dbsize(self):
        "Returns the number of keys in the current database"
        return await self.execute_command('DBSIZE')

    async def debug_object(self, key):
        "Returns version specific meta information about a given key"
        return await self.execute_command('DEBUG OBJECT', key)

    async def flushall(self):
        "Delete all keys in all databases on the current host"
        return await self.execute_command('FLUSHALL')

    async def flushdb(self):
        "Delete all keys in the current database"
        return await self.execute_command('FLUSHDB')

    async def info(self, section=None):
        """
        Returns a dictionary containing information about the Redis server

        The ``section`` option can be used to select a specific section
        of information

        The section option is not supported by older versions of Redis Server,
        and will generate ResponseError
        """
        if section is None:
            return await self.execute_command('INFO')
        else:
            return await self.execute_command('INFO', section)

    async def lastsave(self):
        """
        Return a Python datetime object representing the last time the
        Redis database was saved to disk
        """
        return await self.execute_command('LASTSAVE')

    async def save(self):
        """
        Tell the Redis server to save its data to disk,
        blocking until the save is complete
        """
        return await self.execute_command('SAVE')

    async def shutdown(self):
        "Shutdown the server"
        try:
            await self.execute_command('SHUTDOWN')
        except ConnectionError:
            # a ConnectionError here is expected
            return
        raise RedisError("SHUTDOWN seems to have failed.")

    async def slaveof(self, host=None, port=None):
        """
        Set the server to be a replicated slave of the instance identified
        by the ``host`` and ``port``. If called without arguments, the
        instance is promoted to a master instead.
        """
        if host is None and port is None:
            return await self.execute_command('SLAVEOF', b('NO'), b('ONE'))
        return await self.execute_command('SLAVEOF', host, port)

    async def slowlog_get(self, num=None):
        """
        Get the entries from the slowlog. If ``num`` is specified, get the
        most recent ``num`` items.
        """
        args = ['SLOWLOG GET']
        if num is not None:
            args.append(num)
        return await self.execute_command(*args)

    async def slowlog_len(self):
        "Get the number of items in the slowlog"
        return await self.execute_command('SLOWLOG LEN')

    async def slowlog_reset(self):
        "Remove all items in the slowlog"
        return await self.execute_command('SLOWLOG RESET')

    async def time(self):
        """
        Returns the server time as a 2-item tuple of ints:
        (seconds since epoch, microseconds into this second).
        """
        return await self.execute_command('TIME')

    async def role(self):
        """
        Provide information on the role of a Redis instance in the context of replication,
        by returning if the instance is currently a master, slave, or sentinel.
        The command also returns additional information about the state of the replication
        (if the role is master or slave)
        or the list of monitored master names (if the role is sentinel).
        :return:
        """
        return await self.execute_command('ROLE')
Пример #13
0
def parse_cluster_info(response, **options):
    response = nativestr(response)
    return dict([line.split(':') for line in response.splitlines() if line])
Пример #14
0
def parse_cluster_nodes(resp, **options):
    """
    @see: http://redis.io/commands/cluster-nodes  # string
    @see: http://redis.io/commands/cluster-slaves # list of string
    """
    resp = nativestr(resp)
    current_host = options.get('current_host', '')

    def parse_slots(s):
        slots, migrations = [], []
        for r in s.split(' '):
            if '->-' in r:
                slot_id, dst_node_id = r[1:-1].split('->-', 1)
                migrations.append({
                    'slot': int(slot_id),
                    'node_id': dst_node_id,
                    'state': 'migrating'
                })
            elif '-<-' in r:
                slot_id, src_node_id = r[1:-1].split('-<-', 1)
                migrations.append({
                    'slot': int(slot_id),
                    'node_id': src_node_id,
                    'state': 'importing'
                })
            elif '-' in r:
                start, end = r.split('-')
                slots.extend(range(int(start), int(end) + 1))
            else:
                slots.append(int(r))

        return slots, migrations

    if isinstance(resp, str):
        resp = resp.splitlines()

    nodes = []
    for line in resp:
        parts = line.split(' ', 8)
        self_id, addr, flags, master_id, ping_sent, \
            pong_recv, config_epoch, link_state = parts[:8]

        host, port = addr.rsplit(':', 1)

        node = {
            'id': self_id,
            'host': host or current_host,
            'port': int(port),
            'flags': tuple(flags.split(',')),
            'master': master_id if master_id != '-' else None,
            'ping-sent': int(ping_sent),
            'pong-recv': int(pong_recv),
            'link-state': link_state,
            'slots': [],
            'migrations': [],
        }

        if len(parts) >= 9:
            slots, migrations = parse_slots(parts[8])
            node['slots'], node['migrations'] = tuple(slots), migrations

        nodes.append(node)

    return nodes
Пример #15
0
class StringsCommandMixin:
    RESPONSE_CALLBACKS = dict_merge(
        string_keys_to_dict(
            'MSETNX PSETEX SETEX SETNX',
            bool
        ),
        string_keys_to_dict(
            'BITCOUNT BITPOS DECRBY GETBIT INCRBY '
            'STRLEN SETBIT', int
        ),
        {
            'INCRBYFLOAT': float,
            'MSET': bool_ok,
            'SET': lambda r: r and nativestr(r) == 'OK',
        }
    )

    async def append(self, key, value):
        """
        Appends the string ``value`` to the value at ``key``. If ``key``
        doesn't already exist, create it with a value of ``value``.
        Returns the new length of the value at ``key``.
        """
        return await self.execute_command('APPEND', key, value)

    async def bitcount(self, key, start=None, end=None):
        """
        Returns the count of set bits in the value of ``key``.  Optional
        ``start`` and ``end`` paramaters indicate which bytes to consider
        """
        params = [key]
        if start is not None and end is not None:
            params.append(start)
            params.append(end)
        elif (start is not None and end is None) or \
                (end is not None and start is None):
            raise RedisError("Both start and end must be specified")
        return await self.execute_command('BITCOUNT', *params)

    async def bitop(self, operation, dest, *keys):
        """
        Perform a bitwise operation using ``operation`` between ``keys`` and
        store the result in ``dest``.
        """
        return await self.execute_command('BITOP', operation, dest, *keys)

    async def bitpos(self, key, bit, start=None, end=None):
        """
        Return the position of the first bit set to 1 or 0 in a string.
        ``start`` and ``end`` difines search range. The range is interpreted
        as a range of bytes and not a range of bits, so start=0 and end=2
        means to look at the first three bytes.
        """
        if bit not in (0, 1):
            raise RedisError('bit must be 0 or 1')
        params = [key, bit]

        start is not None and params.append(start)

        if start is not None and end is not None:
            params.append(end)
        elif start is None and end is not None:
            raise RedisError("start argument is not set, "
                             "when end is specified")
        return await self.execute_command('BITPOS', *params)

    def bitfield(self, key):
        return BitField(self, key)

    async def decr(self, name, amount=1):
        """
        Decrements the value of ``key`` by ``amount``.  If no key exists,
        the value will be initialized as 0 - ``amount``
        """
        return await self.execute_command('DECRBY', name, amount)

    async def get(self, name):
        """
        Return the value at key ``name``, or None if the key doesn't exist
        """
        return await self.execute_command('GET', name)

    async def getbit(self, name, offset):
        "Returns a boolean indicating the value of ``offset`` in ``name``"
        return await self.execute_command('GETBIT', name, offset)

    async def getrange(self, key, start, end):
        """
        Returns the substring of the string value stored at ``key``,
        determined by the offsets ``start`` and ``end`` (both are inclusive)
        """
        return await self.execute_command('GETRANGE', key, start, end)

    async def getset(self, name, value):
        """
        Sets the value at key ``name`` to ``value``
        and returns the old value at key ``name`` atomically.
        """
        return await self.execute_command('GETSET', name, value)

    async def incr(self, name, amount=1):
        """
        Increments the value of ``key`` by ``amount``.  If no key exists,
        the value will be initialized as ``amount``
        """
        return await self.execute_command('INCRBY', name, amount)

    async def incrby(self, name, amount=1):
        """
        Increments the value of ``key`` by ``amount``.  If no key exists,
        the value will be initialized as ``amount``
        """

        # An alias for ``incr()``, because it is already implemented
        # as INCRBY redis command.
        return await self.incr(name, amount)

    async def incrbyfloat(self, name, amount=1.0):
        """
        Increments the value at key ``name`` by floating ``amount``.
        If no key exists, the value will be initialized as ``amount``
        """
        return await self.execute_command('INCRBYFLOAT', name, amount)

    async def mget(self, keys, *args):
        """
        Returns a list of values ordered identically to ``keys``
        """
        args = list_or_args(keys, args)
        return await self.execute_command('MGET', *args)

    async def mset(self, *args, **kwargs):
        """
        Sets key/values based on a mapping. Mapping can be supplied as a single
        dictionary argument or as kwargs.
        """
        if args:
            if len(args) != 1 or not isinstance(args[0], dict):
                raise RedisError('MSET requires **kwargs or a single dict arg')
            kwargs.update(args[0])
        items = []
        for pair in iteritems(kwargs):
            items.extend(pair)
        return await self.execute_command('MSET', *items)

    async def msetnx(self, *args, **kwargs):
        """
        Sets key/values based on a mapping if none of the keys are already set.
        Mapping can be supplied as a single dictionary argument or as kwargs.
        Returns a boolean indicating if the operation was successful.
        """
        if args:
            if len(args) != 1 or not isinstance(args[0], dict):
                raise RedisError('MSETNX requires **kwargs or a single '
                                 'dict arg')
            kwargs.update(args[0])
        items = []
        for pair in iteritems(kwargs):
            items.extend(pair)
        return await self.execute_command('MSETNX', *items)

    async def psetex(self, name, time_ms, value):
        """
        Set the value of key ``name`` to ``value`` that expires in ``time_ms``
        milliseconds. ``time_ms`` can be represented by an integer or a Python
        timedelta object
        """
        if isinstance(time_ms, datetime.timedelta):
            ms = int(time_ms.microseconds / 1000)
            time_ms = (time_ms.seconds + time_ms.days * 24 * 3600) * 1000 + ms
        return await self.execute_command('PSETEX', name, time_ms, value)

    async def set(self, name, value, ex=None, px=None, nx=False, xx=False):
        """
        Set the value at key ``name`` to ``value``

        ``ex`` sets an expire flag on key ``name`` for ``ex`` seconds.

        ``px`` sets an expire flag on key ``name`` for ``px`` milliseconds.

        ``nx`` if set to True, set the value at key ``name`` to ``value`` if it
            does not already exist.

        ``xx`` if set to True, set the value at key ``name`` to ``value`` if it
            already exists.
        """
        pieces = [name, value]
        if ex is not None:
            pieces.append('EX')
            if isinstance(ex, datetime.timedelta):
                ex = ex.seconds + ex.days * 24 * 3600
            pieces.append(ex)
        if px is not None:
            pieces.append('PX')
            if isinstance(px, datetime.timedelta):
                ms = int(px.microseconds / 1000)
                px = (px.seconds + px.days * 24 * 3600) * 1000 + ms
            pieces.append(px)

        if nx:
            pieces.append('NX')
        if xx:
            pieces.append('XX')
        return await self.execute_command('SET', *pieces)

    async def setbit(self, name, offset, value):
        """
        Flag the ``offset`` in ``name`` as ``value``. Returns a boolean
        indicating the previous value of ``offset``.
        """
        value = value and 1 or 0
        return await self.execute_command('SETBIT', name, offset, value)

    async def setex(self, name, time, value):
        """
        Set the value of key ``name`` to ``value`` that expires in ``time``
        seconds. ``time`` can be represented by an integer or a Python
        timedelta object.
        """
        if isinstance(time, datetime.timedelta):
            time = time.seconds + time.days * 24 * 3600
        return await self.execute_command('SETEX', name, time, value)

    async def setnx(self, name, value):
        """
        Sets the value of key ``name`` to ``value`` if key doesn't exist
        """
        return await self.execute_command('SETNX', name, value)

    async def setrange(self, name, offset, value):
        """
        Overwrite bytes in the value of ``name`` starting at ``offset`` with
        ``value``. If ``offset`` plus the length of ``value`` exceeds the
        length of the original value, the new value will be larger than before.
        If ``offset`` exceeds the length of the original value, null bytes
        will be used to pad between the end of the previous value and the start
        of what's being injected.

        Returns the length of the new string.
        """
        return await self.execute_command('SETRANGE', name, offset, value)

    async def strlen(self, name):
        """Returns the number of bytes stored in the value of ``name``"""
        return await self.execute_command('STRLEN', name)

    async def substr(self, name, start, end=-1):
        """
        Returns a substring of the string at key ``name``. ``start`` and ``end``
        are 0-based integers specifying the portion of the string to return.
        """
        return await self.execute_command('SUBSTR', name, start, end)
Пример #16
0
def parse_client_list(response, **options):
    clients = []
    for c in nativestr(response).splitlines():
        clients.append(dict([pair.split('=') for pair in c.split(' ')]))
    return clients
Пример #17
0
class ListsCommandMixin:

    RESPONSE_CALLBACKS = dict_merge(
        string_keys_to_dict(
            'BLPOP BRPOP',
            lambda r: r and tuple(r) or None
        ),
        string_keys_to_dict(
            # these return OK, or int if redis-server is >=1.3.4
            'LPUSH RPUSH',
            lambda r: isinstance(r, int) and r or nativestr(r) == 'OK'
        ),
        string_keys_to_dict('LSET LTRIM', bool_ok),
        string_keys_to_dict('LINSERT LLEN LPUSHX RPUSHX', int),
    )

    async def blpop(self, keys, timeout=0):
        """
        LPOP a value off of the first non-empty list
        named in the ``keys`` list.

        If none of the lists in ``keys`` has a value to LPOP, then block
        for ``timeout`` seconds, or until a value gets pushed on to one
        of the lists.

        If timeout is 0, then block indefinitely.
        """
        if timeout is None:
            timeout = 0
        if isinstance(keys, str):
            keys = [keys]
        else:
            keys = list(keys)
        keys.append(timeout)
        return await self.execute_command('BLPOP', *keys)

    async def brpop(self, keys, timeout=0):
        """
        RPOP a value off of the first non-empty list
        named in the ``keys`` list.

        If none of the lists in ``keys`` has a value to LPOP, then block
        for ``timeout`` seconds, or until a value gets pushed on to one
        of the lists.

        If timeout is 0, then block indefinitely.
        """
        if timeout is None:
            timeout = 0
        if isinstance(keys, str):
            keys = [keys]
        else:
            keys = list(keys)
        keys.append(timeout)
        return await self.execute_command('BRPOP', *keys)

    async def brpoplpush(self, src, dst, timeout=0):
        """
        Pop a value off the tail of ``src``, push it on the head of ``dst``
        and then return it.

        This command blocks until a value is in ``src`` or until ``timeout``
        seconds elapse, whichever is first. A ``timeout`` value of 0 blocks
        forever.
        """
        if timeout is None:
            timeout = 0
        return await self.execute_command('BRPOPLPUSH', src, dst, timeout)

    async def lindex(self, name, index):
        """
        Return the item from list ``name`` at position ``index``

        Negative indexes are supported and will return an item at the
        end of the list
        """
        return await self.execute_command('LINDEX', name, index)

    async def linsert(self, name, where, refvalue, value):
        """
        Insert ``value`` in list ``name`` either immediately before or after
        [``where``] ``refvalue``

        Returns the new length of the list on success or -1 if ``refvalue``
        is not in the list.
        """
        return await self.execute_command('LINSERT', name, where, refvalue, value)

    async def llen(self, name):
        """Returns the length of the list ``name``"""
        return await self.execute_command('LLEN', name)

    async def lpop(self, name):
        """RemoveS and returns the first item of the list ``name``"""
        return await self.execute_command('LPOP', name)

    async def lpush(self, name, *values):
        """Pushes ``values`` onto the head of the list ``name``"""
        return await self.execute_command('LPUSH', name, *values)

    async def lpushx(self, name, value):
        """
        Pushes ``value`` onto the head of the list ``name`` if ``name`` exists
        """
        return await self.execute_command('LPUSHX', name, value)

    async def lrange(self, name, start, end):
        """
        Returns a slice of the list ``name`` between
        position ``start`` and ``end``

        ``start`` and ``end`` can be negative numbers just like
        Python slicing notation
        """
        return await self.execute_command('LRANGE', name, start, end)

    async def lrem(self, name, count, value):
        """
        Removes the first ``count`` occurrences of elements equal to ``value``
        from the list stored at ``name``.

        The count argument influences the operation in the following ways:
            count > 0: Remove elements equal to value moving from head to tail.
            count < 0: Remove elements equal to value moving from tail to head.
            count = 0: Remove all elements equal to value.
        """
        return await self.execute_command('LREM', name, count, value)

    async def lset(self, name, index, value):
        """Sets ``position`` of list ``name`` to ``value``"""
        return await self.execute_command('LSET', name, index, value)

    async def ltrim(self, name, start, end):
        """
        Trims the list ``name``, removing all values not within the slice
        between ``start`` and ``end``

        ``start`` and ``end`` can be negative numbers just like
        Python slicing notation
        """
        return await self.execute_command('LTRIM', name, start, end)

    async def rpop(self, name):
        """Removes and return the last item of the list ``name``"""
        return await self.execute_command('RPOP', name)

    async def rpoplpush(self, src, dst):
        """
        RPOP a value off of the ``src`` list and atomically LPUSH it
        on to the ``dst`` list.  Returns the value.
        """
        return await self.execute_command('RPOPLPUSH', src, dst)

    async def rpush(self, name, *values):
        """Pushes ``values`` onto the tail of the list ``name``"""
        return await self.execute_command('RPUSH', name, *values)

    async def rpushx(self, name, value):
        """
        Pushes ``value`` onto the tail of the list ``name`` if ``name`` exists
        """
        return await self.execute_command('RPUSHX', name, value)