예제 #1
0
def lindex_handler(client, argv):
    '''
    Returns the element at index index in the list stored at key. The index is zero-based, so 0
    means the first element, 1 the second element and so on. Negative indices can be used to designate
    elements starting at the tail of the list. Here, -1 means the last element, -2 means the penultimate
    and so forth.

    When the value at key is not a list, an error is returned.

    .. code::
        LINDEX key index

    :return: the requested element, or nil when index is out of range.
    :rtype: str

    '''

    key, index = argv[1], argv[2]
    try:
        index = int(index)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return None
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        return obj[index]
    except IndexError:
        return None
예제 #2
0
def getset_handler(client, argv):
    '''
    Atomically sets key to value and returns the old value stored at key. Returns an error when key
    exists but does not hold a string value.

    .. code::
        GETSET key value

    '''

    key, value = argv[1], argv[2]

    try:
        value = int(value)
    except ValueError:
        pass

    try:
        obj = get_object(client.db, key, type=RedisStringObject)

        orig_value = obj.get_bytes()
        obj.value = value
    except KeyError:
        orig_value = None
        client.db.key_space[key] = RedisStringObject(value)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    return RedisStringObject(orig_value)
예제 #3
0
def lpop_handler(client, argv):
    '''
    Removes and returns the first element of the list stored at key.

    .. code::
        LPOP key

    :return: the value of the first element, or nil when key does not exist.
    :rtype: str

    '''

    key = argv[1]
    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(
            errtype='WRONGTYPE',
            message='Operation against a key holding the wrong kind of value')

    try:
        return obj.pop()
    except IndexError:
        return None
예제 #4
0
def expireat_handler(client, argv):
    '''
    EXPIREAT has the same effect and semantic as EXPIRE, but instead of specifying the number of seconds
    representing the TTL (time to live), it takes an absolute Unix timestamp (seconds since January 1, 1970).

    Please for the specific semantics of the command refer to the documentation of EXPIRE.

    .. code::
        EXPIREAT key timestamp

    :return: 1 if the timeout was set. 0 if key does not exist or the timeout could not be set (see: EXPIRE).
    :rtype: int

    '''

    key, exptime = argv[1], argv[2]
    try:
        exptime = int(exptime)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key)
    except KeyError:
        return 0

    obj.expire_time = exptime
    return 1
예제 #5
0
def lrem_handler(client, argv):
    '''
    Removes the first count occurrences of elements equal to value from the list stored at key. 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.

    For example, LREM list -2 "hello" will remove the last two occurrences of "hello" in the list
    stored at list.

    Note that non-existing keys are treated like empty lists, so when key does not exist, the command
    will always return 0.

    .. code::
        LREM key count value

    :return: the number of removed elements.
    :rtype: int

    '''

    key, count, value = argv[1], argv[2], argv[3]
    try:
        count = int(count)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    if count < 0:
        count *= -1
        objlst = obj[::-1]
        revd = True
    else:
        objlst = obj[:]
        revd = False

    if count == 0:
        count = len(obj)

    counter = 0
    while count:
        try:
            objlst.remove(value)
        except ValueError:
            break
        count -= 1
        counter += 1

    if revd:
        objlst.reverse()
    obj.value = objlst
    return counter
예제 #6
0
def append_handler(client, argv):
    '''
    If key already exists and is a string, this command appends the value at the end of the string.
    If key does not exist it is created and set as an empty string, so APPEND will be similar to SET
    in this special case.

    .. code::
        APPEND key value

    :return: the length of the string after the append operation.
    :rtype: int

    '''

    key, value = argv[1], argv[2]

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
    except KeyError:
        length = len(value)
        try:
            value = int(value)
        except ValueError:
            pass
        client.db.key_space[key] = RedisStringObject(value)
        return length
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    obj.value = obj.get_bytes() + value

    return len(obj.value)
예제 #7
0
def getbit_handler(client, argv):
    '''
    Returns the bit value at offset in the string value stored at key.

    When offset is beyond the string length, the string is assumed to be a contiguous space with 0 bits.
    When key does not exist it is assumed to be an empty string, so offset is always out of range and the
    value is also assumed to be a contiguous space with 0 bits.

    .. code::
        GETBIT key offset

    '''

    key, offset = argv[1], argv[2]

    try:
        offset = int(offset)
    except ValueError:
        abort(message='bit offset is not an integer or out of range')

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        ba = bitarray.bitarray()
        ba.frombytes(obj.get_bytes())
        return int(ba[offset])
    except KeyError:
        return None
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')
    except IndexError:
        return 0
예제 #8
0
def client_handler(client, argv):
    '''

    Client command dispatcher

    .. code::
        CLIENT op args

    '''

    op = argv[1].upper()

    if op == b'KILL':
        return client_kill_handler(client, argv)
    elif op == b'LIST':
        return client_list_handler(client, argv)
    elif op == b'GETNAME':
        return client_getname_handler(client, argv)
    elif op == b'SETNAME':
        return client_setname_handler(client, argv)
    else:
        abort(
            message=
            'Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)'
        )
예제 #9
0
def lpush_handler(client, argv):
    '''
    Insert all the specified values at the head of the list stored at key. If key does not exist,
    it is created as empty list before performing the push operations. When key holds a value that
    is not a list, an error is returned.

    It is possible to push multiple elements using a single command call just specifying multiple
    arguments at the end of the command. Elements are inserted one after the other to the head of
    the list, from the leftmost element to the rightmost element. So for instance the command LPUSH
    mylist a b c will result into a list containing c as first element, b as second element and a
    as third element.

    .. code::
        LPUSH key value [value ...]

    :return: the length of the list after the push operations.
    :rtype: int

    '''

    key, values = argv[1], argv[2:]

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        obj = RedisListObject()
        client.db.key_space[key] = obj
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    # for value in values:
    obj.push(*values)

    return len(obj)
예제 #10
0
def lpushx_handler(client, argv):
    '''
    Inserts value at the head of the list stored at key, only if key already exists and holds a list.
    In contrary to LPUSH, no operation will be performed when key does not yet exist.

    .. code::
        LPUSHX key value

    :return: the length of the list after the push operation.
    :rtype: int

    '''

    key, value = argv[1], argv[2]

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(
            errtype='WRONGTYPE',
            message='Operation against a key holding the wrong kind of value')

    obj.push(value)
    return len(obj)
예제 #11
0
def multi_handler(client, argv):
    '''

    '''

    if client.stat == RedisClientBase.STAT_MULTI:
        abort(message='MULTI calls can not be nested')

    client.stat = RedisClientBase.STAT_MULTI
    return True
예제 #12
0
def lset_handler(client, argv):
    '''

    Sets the list element at index to value. For more information on the index argument, see LINDEX.

    An error is returned for out of range indexes.

    .. code::
        LSET key index value

    '''

    key, index, value = argv[1], argv[2], argv[3]

    try:
        index = int(index)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        abort(message='index out of range')
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    if index < 0:
        index = len(obj) + index

    try:
        obj[index] = value
    except IndexError:
        abort(message='index out of range')
    return True
예제 #13
0
def exec_handler(client, argv):
    '''

    '''

    if client.stat != RedisClientBase.STAT_MULTI:
        abort(message='EXEC without MULTI')

    ret = []
    for cmd in client.multi_command_list:
        ret.append(client.exec_command(cmd))

    client.multi_command_list = []
    client.stat = RedisClientBase.STAT_NORMAL

    return ret
예제 #14
0
def client_getname_handler(client, argv):
    '''
    The CLIENT GETNAME returns the name of the current connection as set by CLIENT SETNAME. Since every
    new connection starts without an associated name, if no name was assigned a null bulk reply is returned.

    .. code::
        CLIENT GETNAME

    '''

    if len(argv) != 2:
        abort(
            message=
            'Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)'
        )

    return client.name
예제 #15
0
def ltrim_handler(client, argv):
    '''
    Trim an existing list so that it will contain only the specified range of elements specified.
    Both start and stop are zero-based indexes, where 0 is the first element of the list (the head),
    1 the next element and so on.

    For example: LTRIM foobar 0 2 will modify the list stored at foobar so that only the first three
    elements of the list will remain.

    start and end can also be negative numbers indicating offsets from the end of the list, where -1 is
    the last element of the list, -2 the penultimate element and so on.

    Out of range indexes will not produce an error: if start is larger than the end of the list, or
    start > end, the result will be an empty list (which causes key to be removed). If end is larger
    than the end of the list, Redis will treat it like the last element of the list.

    .. code::
        LTRIM key start stop

    '''

    key, start, stop = argv[1], argv[2], argv[3]

    try:
        start = int(start)
        stop = int(stop)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return True
    except TypeError:
        abort(
            errtype='WRONGTYPE',
            message='Operation against a key holding the wrong kind of value')

    if start < 0:
        start = len(obj) + start
    if stop < 0:
        stop = len(obj) + stop

    obj.value = obj[start:stop]
    return True
예제 #16
0
def get_handler(client, argv):
    '''
    Get the value of key. If the key does not exist the special value nil is returned.
    An error is returned if the value stored at key is not a string, because GET only handles string values.

    .. code::
        GET key

    '''

    key = argv[1]
    try:
        obj = get_object(client.db, key, RedisStringObject)
        return obj
    except KeyError:
        return None
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')
예제 #17
0
def ltrim_handler(client, argv):
    '''
    Trim an existing list so that it will contain only the specified range of elements specified.
    Both start and stop are zero-based indexes, where 0 is the first element of the list (the head),
    1 the next element and so on.

    For example: LTRIM foobar 0 2 will modify the list stored at foobar so that only the first three
    elements of the list will remain.

    start and end can also be negative numbers indicating offsets from the end of the list, where -1 is
    the last element of the list, -2 the penultimate element and so on.

    Out of range indexes will not produce an error: if start is larger than the end of the list, or
    start > end, the result will be an empty list (which causes key to be removed). If end is larger
    than the end of the list, Redis will treat it like the last element of the list.

    .. code::
        LTRIM key start stop

    '''

    key, start, stop = argv[1], argv[2], argv[3]

    try:
        start = int(start)
        stop = int(stop)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return True
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    if start < 0:
        start = len(obj) + start
    if stop < 0:
        stop = len(obj) + stop

    obj.value = obj[start:stop]
    return True
예제 #18
0
파일: server.py 프로젝트: qingant/redis-py
            def __wrapper(client, argv):

                if nargs is not None:
                    if isinstance(nargs, int):
                        if len(argv) - 1 != nargs:
                            abort(
                                message=
                                "wrong number of arguments for '%s' command" %
                                cmd.decode())
                    elif isinstance(nargs, types.FunctionType):
                        if not nargs(len(argv) - 1):
                            abort(
                                message=
                                "wrong number of arguments for '%s' command" %
                                cmd.decode())

                try:
                    ret = func(client, argv)

                    if isinstance(ret, RedisObject):
                        return ret.serialize()
                    elif isinstance(ret, RedisSerializationObject):
                        return ret
                    elif ret is True:
                        return RedisSimpleStringSerializationObject('OK')
                    elif isinstance(ret, int):
                        # This line shouldn't put before the ``ret is True``
                        # **Cuz True is an integer**
                        return RedisIntegerSerializationObject(ret)
                    elif isinstance(ret, (list, types.GeneratorType)):
                        return RedisListSerializationObject(ret)
                    elif ret is None:
                        return RedisBulkStringSerializationObject(None)
                    else:
                        raise ValueError('Invalid reply %s' % ret)
                except CommandNotFoundError as e:
                    return RedisErrorStringSerializationObject(errtype='ERR',
                                                               message=str(e))
                except CommandError as e:
                    errtype, message = e.args
                    return RedisErrorStringSerializationObject(errtype=errtype,
                                                               message=message)
예제 #19
0
def client_pause_handler(client, argv):
    '''
    CLIENT PAUSE is a connections control command able to suspend all the Redis clients for the specified
    amount of time (in milliseconds).

    The command performs the following actions:

    * It stops processing all the pending commands from normal and pub/sub clients. However interactions with
      slaves will continue normally.

    * However it returns OK to the caller ASAP, so the CLIENT PAUSE command execution is not paused by itself.

    * When the specified amount of time has elapsed, all the clients are unblocked: this will trigger the
      processing of all the commands accumulated in the query buffer of every client during the pause.

    This command is useful as it makes able to switch clients from a Redis instance to another one in a
    controlled way. For example during an instance upgrade the system administrator could do the following:

    * Pause the clients using CLIENT PAUSE

    * Wait a few seconds to make sure the slaves processed the latest replication stream from the master.

    * Turn one of the slaves into a master.

    * Reconfigure clients to connect with the new master.

    It is possible to send CLIENT PAUSE in a MULTI/EXEC block together with the INFO replication command in
    order to get the current master offset at the time the clients are blocked. This way it is possible to wait
    for a specific offset in the slave side in order to make sure all the replication stream was processed.

    .. code::
        CLIENT PAUSE

    '''

    if len(argv) != 2:
        abort(
            message=
            'Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)'
        )

    raise NotImplementedError()
예제 #20
0
def linsert(client, argv):
    '''
    Inserts value in the list stored at key either before or after the reference value pivot.

    When key does not exist, it is considered an empty list and no operation is performed.

    An error is returned when key exists but does not hold a list value.

    .. code::
        LINSERT key BEFORE|AFTER pivot value

    :return: the length of the list after the insert operation, or -1 when the value pivot was not found.
    :rtype: int

    '''

    key, op, pivot, value = argv[1], argv[2].upper(), argv[3], argv[4]

    if op not in (b'BEFORE', b'AFTER'):
        abort(message='syntax error')

    if op == b'BEFORE':
        op_func = lambda index: index
    else:
        op_func = lambda index: index + 1

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return None
    except TypeError:
        abort(
            errtype='WRONGTYPE',
            message='Operation against a key holding the wrong kind of value')

    try:
        index = obj.index(pivot)
        obj.insert(op_func(index), value)
    except ValueError:
        return None

    return len(obj)
예제 #21
0
def setex_handler(client, argv):
    '''
    Set key to hold the string value and set key to timeout after a given number of seconds.
    This command is equivalent to executing the following commands:

    .. code::
        SETEX key seconds value

    '''

    key, seconds, value = argv[1], argv[2], argv[3]
    try:
        seconds = int(seconds)
        if seconds <= 0:
            abort(message='invalid expire time in SETEX')
    except ValueError:
        abort(message='value is not an integer or out of range')

    client.db.key_space[key] = RedisStringObject(value, expire_time=time.time() + seconds)
    return True
예제 #22
0
def mset_handler(client, argv):
    '''
    Sets the given keys to their respective values. MSET replaces existing values with new values,
    just as regular SET. See MSETNX if you don't want to overwrite existing values.

    MSET is atomic, so all given keys are set at once. It is not possible for clients to see that
    some of the keys were updated while others are unchanged.

    .. code::
        MSET key value [key value ...]

    '''

    if len(argv) & 1 == 0:
        abort(message='wrong number of arguments for MSET')

    for key, value in group_iter(argv[1:], n=2):
        client.db.key_space[key] = RedisStringObject(value=value)

    return True
예제 #23
0
def incr_handler(client, argv):
    '''
    Increments the number stored at key by one. If the key does not exist, it is set to 0 before
    performing the operation. An error is returned if the key contains a value of the wrong type or
    contains a string that can not be represented as integer. This operation is limited to 64 bit
    signed integers.

    Note: this is a string operation because Redis does not have a dedicated integer type. The string
    stored at the key is interpreted as a base-10 64 bit signed integer to execute the operation.

    Redis stores integers in their integer representation, so for string values that actually hold an
    integer, there is no overhead for storing the string representation of the integer.

    .. code::
        INCR key

    :return: the value of key after the increment
    :rtype: int

    '''
    key = argv[1]

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        value = obj.get_integer()
    except KeyError:
        value = 0
        obj = RedisStringObject(value)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        value = obj.get_integer()
    except ValueError:
        abort(message='value is not an integer or out of range')

    value += 1

    obj.value = value
    client.db.key_space[key] = obj
    return obj
예제 #24
0
def llen_handler(client, argv):
    '''
    Returns the length of the list stored at key. If key does not exist, it is interpreted as an empty
    list and 0 is returned. An error is returned when the value stored at key is not a list.

    .. code::
        LLEN key

    :return: the length of the list at key.
    :rtype: int
    '''

    key = argv[1]
    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    return len(obj)
예제 #25
0
def client_list_handler(client, argv):
    '''
    The CLIENT LIST command returns information and statistics about the client connections server in a
    mostly human readable format.

    .. code::
        CLIENT LIST

    :return:  a unique string, formatted as follows:
              * One client connection per line (separated by LF)
              * Each line is composed of a succession of property=value fields separated by a space character.

    '''

    if len(argv) != 2:
        abort(
            message=
            'Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)'
        )

    return client.server.get_clients_info_str()
예제 #26
0
def setrange_handler(client, argv):
    '''
    Overwrites part of the string stored at key, starting at the specified offset, for the entire
    length of value. If the offset is larger than the current length of the string at key, the string
    is padded with zero-bytes to make offset fit. Non-existing keys are considered as empty strings,
    so this command will make sure it holds a string large enough to be able to set value at offset.

    Note that the maximum offset that you can set is 229 -1 (536870911), as Redis Strings are limited
    to 512 megabytes. If you need to grow beyond this size, you can use multiple keys.

    .. code::
        SETRANGE key offset value

    :return: the length of the string after it was modified by the command.
    :rtype: int

    '''

    key, offset, value = argv[1], argv[2], argv[3]
    try:
        offset = int(offset)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisStringObject)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')
    except KeyError:
        obj = RedisStringObject()

    stor_value = obj.get_bytes()
    if len(stor_value) < offset:
        stor_value += bytes(offset - len(stor_value)) + value
    else:
        stor_value = stor_value[0:offset + 1] + value

    obj.value = stor_value
    client.db.key_space[key] = obj
    return len(stor_value)
예제 #27
0
def linsert(client, argv):
    '''
    Inserts value in the list stored at key either before or after the reference value pivot.

    When key does not exist, it is considered an empty list and no operation is performed.

    An error is returned when key exists but does not hold a list value.

    .. code::
        LINSERT key BEFORE|AFTER pivot value

    :return: the length of the list after the insert operation, or -1 when the value pivot was not found.
    :rtype: int

    '''

    key, op, pivot, value = argv[1], argv[2].upper(), argv[3], argv[4]

    if op not in (b'BEFORE', b'AFTER'):
        abort(message='syntax error')

    if op == b'BEFORE':
        op_func = lambda index: index
    else:
        op_func = lambda index: index + 1

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return None
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        index = obj.index(pivot)
        obj.insert(op_func(index), value)
    except ValueError:
        return None

    return len(obj)
예제 #28
0
def strlen_handler(client, argv):
    '''
    Returns the length of the string value stored at key. An error is returned when key holds a non-string value.

    .. code::
        STRLEN key

    :return: the length of the string at key, or 0 when key does not exist.
    :rtype: int

    '''

    key = argv[1]

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
    except KeyError:
        return 0
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    return len(obj.get_bytes())
예제 #29
0
def lrange_handler(client, argv):
    '''
    Returns the specified elements of the list stored at key. The offsets start and stop are zero-based
    indexes, with 0 being the first element of the list (the head of the list), 1 being the next element
    and so on.

    These offsets can also be negative numbers indicating offsets starting at the end of the list. For
    example, -1 is the last element of the list, -2 the penultimate, and so on.

    .. code::
        LRANGE key start stop

    :return: list of elements in the specified range.
    :rtype: list

    '''

    key, start, stop = argv[1], argv[2], argv[3]
    try:
        start = int(start)
        stop = int(stop)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return []
    except TypeError:
        abort(
            errtype='WRONGTYPE',
            message='Operation against a key holding the wrong kind of value')

    if start < 0:
        start = len(obj) + start
    if stop < 0:
        stop = len(obj) + stop

    return obj[start:stop + 1]
예제 #30
0
def client_setname_handler(client, argv):
    '''
    The CLIENT SETNAME command assigns a name to the current connection.

    The assigned name is displayed in the output of CLIENT LIST so that it is possible to identify the
    client that performed a given connection.

    For instance when Redis is used in order to implement a queue, producers and consumers of messages
    may want to set the name of the connection according to their role.

    There is no limit to the length of the name that can be assigned if not the usual limits of the Redis
    string type (512 MB). However it is not possible to use spaces in the connection name as this would
    violate the format of the CLIENT LIST reply.

    It is possible to entirely remove the connection name setting it to the empty string, that is not a valid
    connection name since it serves to this specific purpose.

    The connection name can be inspected using CLIENT GETNAME.

    Every new connection starts without an assigned name.

    Tip: setting names to connections is a good way to debug connection leaks due to bugs in the application
    using Redis.

    .. code::
        CLIENT SETNAME name

    '''

    if len(argv) != 3:
        abort(
            message=
            'Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)'
        )

    client.name = argv[2].decode()

    return True
예제 #31
0
def client_kill_handler(client, argv):
    '''
    The CLIENT KILL command closes a given client connection identified by ip:port.

    The ip:port should match a line returned by the CLIENT LIST command.

    .. code::
        CLIENT KILL ip:port

    '''

    if len(argv) != 3:
        abort(
            message=
            'Syntax error, try CLIENT (LIST | KILL ip:port | GETNAME | SETNAME connection-name)'
        )

    try:
        client.server.kill_client(argv[2].decode())
    except KeyError:
        abort(message='No such client')

    return True
예제 #32
0
def llen_handler(client, argv):
    '''
    Returns the length of the list stored at key. If key does not exist, it is interpreted as an empty
    list and 0 is returned. An error is returned when the value stored at key is not a list.

    .. code::
        LLEN key

    :return: the length of the list at key.
    :rtype: int
    '''

    key = argv[1]
    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(
            errtype='WRONGTYPE',
            message='Operation against a key holding the wrong kind of value')

    return len(obj)
예제 #33
0
def lrange_handler(client, argv):
    '''
    Returns the specified elements of the list stored at key. The offsets start and stop are zero-based
    indexes, with 0 being the first element of the list (the head of the list), 1 being the next element
    and so on.

    These offsets can also be negative numbers indicating offsets starting at the end of the list. For
    example, -1 is the last element of the list, -2 the penultimate, and so on.

    .. code::
        LRANGE key start stop

    :return: list of elements in the specified range.
    :rtype: list

    '''

    key, start, stop = argv[1], argv[2], argv[3]
    try:
        start = int(start)
        stop = int(stop)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return []
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    if start < 0:
        start = len(obj) + start
    if stop < 0:
        stop = len(obj) + stop

    return obj[start:stop + 1]
예제 #34
0
def msetnx_handler(client, argv):
    '''
    Sets the given keys to their respective values. MSETNX will not perform any operation at all even
    if just a single key already exists.

    Because of this semantic MSETNX can be used in order to set different keys representing different
    fields of an unique logic object in a way that ensures that either all the fields or none at all
    are set.

    MSETNX is atomic, so all given keys are set at once. It is not possible for clients to see that some
    of the keys were updated while others are unchanged.

    .. code::
        MSETNX key value [key value ...]

    :return: 1 if the all the keys were set. 0 if no key was set (at least one key already existed).
    :rtype: int

    '''

    if len(argv) & 1 == 0:
        abort(message='wrong number of arguments for MSET')

    all_set = True
    for key, value in group_iter(argv[1:], n=2):
        try:
            get_object(client.db, key)
            all_set = False
            break
        except KeyError:
            pass

    if all_set:
        for key, value in group_iter(argv[1:], n=2):
            client.db.key_space[key] = RedisStringObject(value=value)

    return int(all_set)
예제 #35
0
def decr_handler(client, argv):
    '''
    Decrements the number stored at key by one. If the key does not exist, it is set to 0 before
    performing the operation. An error is returned if the key contains a value of the wrong type
    or contains a string that can not be represented as integer. This operation is limited to 64
    bit signed integers.

    See INCR for extra information on increment/decrement operations.

    .. code::
        DECR key

    :return: the value of key after the decrement
    :rtype: int

    '''
    key = argv[1]

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        value = obj.get_integer()
    except KeyError:
        value = 0
        obj = RedisStringObject(value)
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        value = obj.get_integer()
    except ValueError:
        abort(message='value is not an integer or out of range')

    value -= 1

    obj.value = value
    # client.db.key_space[key] = obj
    return obj
예제 #36
0
def expire_handler(client, argv):
    '''
    Set a timeout on key. After the timeout has expired, the key will automatically be deleted. A key
    with an associated timeout is often said to be volatile in Redis terminology.

    The timeout is cleared only when the key is removed using the DEL command or overwritten using the
    SET or GETSET commands. This means that all the operations that conceptually alter the value stored
    at the key without replacing it with a new one will leave the timeout untouched. For instance,
    incrementing the value of a key with INCR, pushing a new value into a list with LPUSH, or altering the
    field value of a hash with HSET are all operations that will leave the timeout untouched.

    The timeout can also be cleared, turning the key back into a persistent key, using the PERSIST command.

    If a key is renamed with RENAME, the associated time to live is transferred to the new key name.

    If a key is overwritten by RENAME, like in the case of an existing key Key_A that is overwritten by a call
    like RENAME Key_B Key_A, it does not matter if the original Key_A had a timeout associated or not, the new
    key Key_A will inherit all the characteristics of Key_B.

    .. code::
        EXPIRE key time
    '''

    key, exptime = argv[1], argv[2]
    try:
        exptime = int(exptime)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key)
    except KeyError:
        return 0

    obj.expire_time = time.time() + exptime
    return 1
예제 #37
0
def pexpireat_handler(client, argv):
    '''
    PEXPIREAT has the same effect and semantic as EXPIREAT, but the Unix time at which the key will
    expire is specified in milliseconds instead of seconds.

    .. code::
        PEXPIREAT key milliseconds-timestamp

    '''

    key, milliseconds = argv[1], argv[2]

    try:
        milliseconds = int(milliseconds)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key)
    except KeyError:
        return 0

    obj.expire_time = milliseconds / 1000.0
    return 1
예제 #38
0
def pexpire_handler(client, argv):
    '''
    This command works exactly like EXPIRE but the time to live of the key is specified in milliseconds
    instead of seconds.

    .. code::
        PEXPIRE key milliseconds

    '''

    key, milliseconds = argv[1], argv[2]

    try:
        milliseconds = int(milliseconds)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key)
    except KeyError:
        return 0

    obj.expire_time = time.time() + milliseconds / 1000.0
    return 1
예제 #39
0
def lpop_handler(client, argv):
    '''
    Removes and returns the first element of the list stored at key.

    .. code::
        LPOP key

    :return: the value of the first element, or nil when key does not exist.
    :rtype: str

    '''

    key = argv[1]
    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        return obj.pop()
    except IndexError:
        return None
예제 #40
0
def lpushx_handler(client, argv):
    '''
    Inserts value at the head of the list stored at key, only if key already exists and holds a list.
    In contrary to LPUSH, no operation will be performed when key does not yet exist.

    .. code::
        LPUSHX key value

    :return: the length of the list after the push operation.
    :rtype: int

    '''

    key, value = argv[1], argv[2]

    try:
        obj = get_object(client.db, key, RedisListObject)
    except KeyError:
        return 0
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    obj.push(value)
    return len(obj)
예제 #41
0
def getrange_handler(client, argv):
    '''
    Returns the substring of the string value stored at key, determined by the offsets start and end
    (both are inclusive). Negative offsets can be used in order to provide an offset starting from the
    end of the string. So -1 means the last character, -2 the penultimate and so forth.

    The function handles out of range requests by limiting the resulting range to the actual length of
    the string.

    .. code::
        GETRANGE key start end

    '''

    key, start, end = argv[1], argv[2], argv[3]

    try:
        start = int(start)
        end = int(end)
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, type=RedisStringObject)

    except KeyError:
        return b''
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    length = len(obj)
    if start < 0:
        start = length + start
    if end < 0:
        end = length + end
    return obj.get_range(start, end)
예제 #42
0
def incrbyfloat_handler(client, argv):
    '''
    Increment the string representing a floating point number stored at key by the specified increment.
    If the key does not exist, it is set to 0 before performing the operation. An error is returned if
    one of the following conditions occur:

    * The key contains a value of the wrong type (not a string).
    * The current key content or the specified increment are not parsable as a double precision floating
      point number.

    If the command is successful the new incremented value is stored as the new value of the key (replacing
    the old one), and returned to the caller as a string.

    Both the value already contained in the string key and the increment argument can be optionally provided
    in exponential notation, however the value computed after the increment is stored consistently in the
    same format, that is, an integer number followed (if needed) by a dot, and a variable number of digits
    representing the decimal part of the number. Trailing zeroes are always removed.

    The precision of the output is fixed at 17 digits after the decimal point regardless of the actual internal
    precision of the computation.

    .. code::
        INCRBYFLOAT key increment

    :return: the value of key after the increment.
    :rtype: bytes

    '''

    key, increment = argv[1], argv[2]

    try:
        increment = Decimal(increment.decode())
    except InvalidOperation:
        abort(message='value is not a valid float')

    try:
        obj = get_object(client.db, key, type=RedisStringObject)
        value = obj.get_integer()
    except KeyError:
        value = 0
        obj = RedisStringObject()
    except TypeError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    try:
        value = obj.get_decimal()
    except InvalidOperation:
        abort(message='value is not a valid float')

    value += increment

    obj.value = value
    client.db.key_space[key] = obj
    return obj
예제 #43
0
def bitcount_handler(client, argv):
    '''
    Count the number of set bits (population counting) in a string.

    By default all the bytes contained in the string are examined. It is possible to specify the counting
    operation only in an interval passing the additional arguments start and end.

    Like for the GETRANGE command start and end can contain negative values in order to index bytes starting
    from the end of the string, where -1 is the last byte, -2 is the penultimate, and so forth.

    Non-existent keys are treated as empty strings, so the command will return zero.

    .. code::
        BITCOUNT key [start end]

    :return: The number of bits set to 1.
    :rtype: int

    '''

    key, start, end = argv[1], None, None
    if len(argv) == 3 or len(argv) > 4:
        abort(message='syntax error')
    try:
        if len(argv) == 4:
            start, end = int(argv[2]), int(argv[3])
    except ValueError:
        abort(message='value is not an integer or out of range')

    try:
        obj = get_object(client.db, key, RedisStringObject)
    except KeyError:
        return 0
    except ValueError:
        abort(errtype='WRONGTYPE', message='Operation against a key holding the wrong kind of value')

    if end == -1:
        end = None
    elif end is not None:
        end += 1

    ba = bitarray.bitarray()
    ba.frombytes(obj.get_bytes()[start:end])
    return ba.count()