Exemplo n.º 1
0
    def run_in_thread(self, sleep_time=0):
        for channel, handler in iteritems(self.channels):
            if handler is None:
                raise PubSubError("Channel: '%s' has no handler registered")
        for pattern, handler in iteritems(self.patterns):
            if handler is None:
                raise PubSubError("Pattern: '%s' has no handler registered")
        pubsub = self

        class WorkerThread(threading.Thread):
            def __init__(self, *args, **kwargs):
                super(WorkerThread, self).__init__(*args, **kwargs)
                self._running = False

            def run(self):
                if self._running:
                    return
                self._running = True
                while self._running and pubsub.subscribed:
                    pubsub.get_message(ignore_subscribe_messages=True)
                    mod_time.sleep(sleep_time)

            def stop(self):
                self._running = False
                self.join()

        thread = WorkerThread()
        thread.start()
        return thread
Exemplo n.º 2
0
 def _rc_msetnx(self, mapping):
     """
     Sets each key in the ``mapping`` dict to its corresponding value if
     none of the keys are already set
     """
     for k, v in iteritems(mapping):
         if self.exists(k):
             return False
     result = True
     for k, v in iteritems(mapping):
         result = result and self.set(k, v)
     return result
Exemplo n.º 3
0
    def zadd(self, name, *args, **kwargs):
        """
        NOTE: The order of arguments differs from that of the official ZADD
        command. For backwards compatability, this method accepts arguments
        in the form of name1, score1, name2, score2, while the official Redis
        documents expects score1, name1, score2, name2.
        If you're looking to use the standard syntax, consider using the
        StrictRedis class. See the API Reference section of the docs for more
        information.
        Set any number of element-name, score pairs to the key ``name``. Pairs
        can be specified in two ways:
        As *args, in the form of: name1, score1, name2, score2, ...
        or as **kwargs, in the form of: name1=score1, name2=score2, ...
        The following example would add four values to the 'my-key' key:
        redis.zadd('my-key', 'name1', 1.1, 'name2', 2.2, name3=3.3, name4=4.4)
        """
        pieces = []

        if args:
            if len(args) % 2 != 0:
                raise RedisError("ZADD requires an equal number of values and scores")
            pieces.extend(reversed(args))

        for pair in iteritems(kwargs):
            pieces.append(pair[1])
            pieces.append(pair[0])

        return self.execute_command('ZADD', name, *pieces)
Exemplo n.º 4
0
    def test_binary_lists(self, r):
        mapping = {
            b('foo bar'): [b('1'), b('2'), b('3')],
            b('foo\r\nbar\r\n'): [b('4'), b('5'), b('6')],
            b('foo\tbar\x07'): [b('7'), b('8'), b('9')],
        }
        # fill in lists
        for key, value in iteritems(mapping):
            r.rpush(key, *value)

        # check that KEYS returns all the keys as they are
        assert sorted(r.keys('*')) == sorted(list(iterkeys(mapping)))

        # check that it is possible to get list content by key name
        for key, value in iteritems(mapping):
            assert r.lrange(key, 0, -1) == value
Exemplo n.º 5
0
    def test_binary_lists(self, r):
        mapping = {
            b("foo bar"): [b("1"), b("2"), b("3")],
            b("foo\r\nbar\r\n"): [b("4"), b("5"), b("6")],
            b("foo\tbar\x07"): [b("7"), b("8"), b("9")],
        }
        # fill in lists
        for key, value in iteritems(mapping):
            r.rpush(key, *value)

        # check that KEYS returns all the keys as they are
        assert sorted(r.keys("*")) == sorted(list(iterkeys(mapping)))

        # check that it is possible to get list content by key name
        for key, value in iteritems(mapping):
            assert r.lrange(key, 0, -1) == value
Exemplo n.º 6
0
 def test_msetnx_kwargs(self, r):
     d = {"a": b("1"), "b": b("2"), "c": b("3")}
     assert r.msetnx(**d)
     d2 = {"a": b("x"), "d": b("4")}
     assert not r.msetnx(**d2)
     for k, v in iteritems(d):
         assert r[k] == v
     assert r.get("d") is None
Exemplo n.º 7
0
    def mset(self, *args, **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])

        mapping = {self.appendKeys(key): value for key, value in iteritems(kwargs)}
        return self.redis.mset(mapping)
Exemplo n.º 8
0
 def test_msetnx_kwargs(self, r):
     d = {'a': b('1'), 'b': b('2'), 'c': b('3')}
     assert r.msetnx(**d)
     d2 = {'a': b('x'), 'd': b('4')}
     assert not r.msetnx(**d2)
     for k, v in iteritems(d):
         assert r[k] == v
     assert r.get('d') is None
Exemplo n.º 9
0
 def on_connect(self, connection):
     "Re-subscribe to any channels and patterns previously subscribed to"
     # NOTE: for python3, we can't pass bytestrings as keyword arguments
     # so we need to decode channel/pattern names back to unicode strings
     # before passing them to [p]subscribe.
     if self.channels:
         channels = {}
         for k, v in iteritems(self.channels):
             if not self.decode_responses:
                 k = k.decode(self.encoding, self.encoding_errors)
             channels[k] = v
         self.subscribe(**channels)
     if self.patterns:
         patterns = {}
         for k, v in iteritems(self.patterns):
             if not self.decode_responses:
                 k = k.decode(self.encoding, self.encoding_errors)
             patterns[k] = v
         self.psubscribe(**patterns)
Exemplo n.º 10
0
 def hmset(self, name, mapping):
     """
     Set key to value within hash ``name`` for each corresponding
     key and value from the ``mapping`` dict.
     """
     if not mapping:
         raise DataError("'hmset' with 'mapping' of length 0")
     items = []
     for pair in iteritems(mapping):
         items.extend(pair)
     return self.execute_command('HMSET', name, *items)
Exemplo n.º 11
0
    def _rc_keys(self, pattern='*'):
        "Returns a list of keys matching ``pattern``"

        result = []
        for alias, redisent in iteritems(self.redises):
            if alias.find('_slave') == -1:
                continue

            result.extend(redisent.keys(pattern))

        return result
Exemplo n.º 12
0
    def _rc_dbsize(self):
        "Returns the number of keys in the current database"

        result = 0
        for alias, redisent in iteritems(self.redises):
            if alias.find('_slave') == -1:
                continue

            result += redisent.dbsize()

        return result
Exemplo n.º 13
0
    def from_url(cls, url, **kwargs):
        """
        return a sentinel object from url
        ``url`` sentinels://<host1>:<port1>,<host2>:<port2>/<db>?<querystring>
        ``param`` kwargs: parameters for constructing Sentinel object
        ``return`` tuple of sentinel object, db from url, service_name in url

        url example::
            sentinels://10.1.2.122:17700
            sentinels://10.1.2.122:17700, 10.1.2.128:17700
            sentinels://node1:17700,node2:17700

        if db appears in both querystring and path, use path as first choice.
        For example:
            sentinels://node1:17700,node2:17700/1?db=2 return db 2
        """
        url_string = url
        url = urlparse(url)

        if url.scheme != "sentinels":
            raise Exception("sentinel url must start with sentinels://")

        # in python2.6, custom URL schemes don't recognize querystring values
        # they're left as part of the url.path.
        if '?' in url.path and not url.query:
            # chop the querystring including the ? off the end of the url
            # and reparse it.
            qs = url.path.split('?', 1)[1]
            url = urlparse(url_string[:-(len(qs) + 1)])
        else:
            qs = url.query

        url_options = {}  # redis options

        for name, value in iteritems(parse_qs(qs)):
            if value and len(value) > 0:
                url_options[name] = value[0]

        service_name = url_options.get('service_name')

        sentinels = [host.split(':') for host in url.netloc.split(',')]

        # If there's a path argument, use it as the db argument if a
        # querystring value wasn't specified
        db_from_url = url_options.get('db')
        if db_from_url is None and url.path:
            try:
                db_from_url = int(url.path.replace('/', ''))
            except (AttributeError, ValueError):
                pass

        return Sentinel(sentinels, **kwargs), db_from_url, service_name
Exemplo n.º 14
0
 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 self.execute_command('MSET', *items)
Exemplo n.º 15
0
    def __init__(self, cluster={}, db=0):
        #raise exception when wrong server hash
        if 'nodes' not in cluster or 'master_of' not in cluster:
            raise Exception(
                "rediscluster: Please set a correct array of redis cluster.")

        self.cluster = cluster
        self.no_servers = len(cluster['master_of'])
        slaves = dictvalues(cluster['master_of'])
        self.redises = {}
        #connect to all servers
        for alias, server in iteritems(cluster['nodes']):
            try:
                self.__redis = redis.StrictRedis(db=db, **server)
                sla = self.__redis.config_get('slaveof')['slaveof']
                if alias in slaves and sla == '':
                    raise redis.DataError(
                        "rediscluster: server %s is not a slave." % (server,))
            except Exception as e:
                #if node is slave and is down, replace its connection with its master's
                try:
                    ms = [k for k, v in iteritems(cluster['master_of'])
                          if v == alias and (sla != '' or cluster['nodes'][k] == cluster['nodes'][v])][0]
                except IndexError:
                    ms = None

                if ms is not None:
                    try:
                        self.__redis = redis.StrictRedis(
                            db=db, **cluster['nodes'][ms])
                        self.__redis.info()
                    except Exception as e:
                        raise redis.ConnectionError("rediscluster cannot connect to: %s %s" % (cluster['nodes'][ms], e))

                else:
                    raise redis.ConnectionError(
                        "rediscluster cannot connect to: %s %s" % (server, e))

            self.redises[alias] = self.__redis
Exemplo n.º 16
0
 def __init__(self, sentinels, min_other_sentinels=0, sentinel_kwargs=None,
              **connection_kwargs):
     # if sentinel_kwargs isn't defined, use the socket_* options from
     # connection_kwargs
     if sentinel_kwargs is None:
         sentinel_kwargs = dict([(k, v)
                                 for k, v in iteritems(connection_kwargs)
                                 if k.startswith('socket_')
                                 ])
     self.sentinel_kwargs = sentinel_kwargs
     self.sentinels = [StrictRedis(hostname, port, **self.sentinel_kwargs)
                       for hostname, port in sentinels]
     self.min_other_sentinels = min_other_sentinels
     self.connection_kwargs = connection_kwargs
    def mset(self, *args, **kwargs):
        """
        Sets key/values based on a mapping. Mapping can be supplied as a single
        dictionary argument or as kwargs.

        Cluster impl: Itterate over all items and do SET on each (k,v) pair
        """
        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])
        for pair in iteritems(kwargs):
            self.set(pair[0], pair[1])
        return True
Exemplo n.º 18
0
    def run_in_thread(self, sleep_time=0):
        '''
        Replacement for the default PubSub run_in_thread method from
        http://github.com/andymccurdy/redis-py/master/redis/client.py
        '''
        for channel, handler in iteritems(self.channels):
            if handler is None:
                raise PubSubError("Channel: '%s' has no handler registered" % channel)
        for pattern, handler in iteritems(self.channels):
            if handler is None:
                raise PubSubError("Pattern: '%s' has no handler registered" % pattern)
        pubsub = self

        class WorkerThread(threading.Thread):
            ''' Listens for messages on subscriptions '''
            def __init__(self, *args, **kwargs):
                super(WorkerThread, self).__init__(*args, **kwargs)
                self.daemon = True
                self._running = False

            def run(self):
                ''' loop while running on subscriptions '''
                if self._running:
                    return
                self._running = True
                while self._running and pubsub.subscribed:
                    pubsub.get_message(ignore_subscribe_messages=True)
                    mod_time.sleep(sleep_time)

            def stop(self):
                ''' stops the main loop '''
                self._running = False
                self.join()

        thread = WorkerThread()
        thread.start()
        return thread
Exemplo n.º 19
0
 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 self.execute_command('MSETNX', *items)
Exemplo n.º 20
0
    def mset(self, *args, **kwargs):
        """
        Sets key/values based on a mapping. Mapping can be supplied as a single
        dictionary argument or as kwargs.

        Cluster impl:
            Itterate over all items and do SET on each (k,v) pair

            Operation is no longer atomic.
        """
        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])

        for pair in iteritems(kwargs):
            self.set(pair[0], pair[1])

        return True
Exemplo n.º 21
0
    def __init__(self,
                 sentinels,
                 min_other_sentinels=0,
                 sentinel_kwargs=None,
                 **connection_kwargs):
        # if sentinel_kwargs isn't defined, use the socket_* options from
        # connection_kwargs
        if sentinel_kwargs is None:
            sentinel_kwargs = dict([(k, v)
                                    for k, v in iteritems(connection_kwargs)
                                    if k.startswith('socket_')])
        self.sentinel_kwargs = sentinel_kwargs

        self.sentinels = [
            StrictRedis(hostname, port, **self.sentinel_kwargs)
            for hostname, port in sentinels
        ]
        self.min_other_sentinels = min_other_sentinels
        self.connection_kwargs = connection_kwargs
Exemplo n.º 22
0
        def function(*args, **kwargs):
            if name not in StrictRedisCluster._loop_keys:
                if name in StrictRedisCluster._tag_keys and not isinstance(args[0], list):
                    try:
                        return getattr(self, '_rc_' + name)(*args, **kwargs)
                    except AttributeError:
                        raise redis.DataError("rediscluster: Command %s Not Supported (each key name has its own node)" % name)

                #get the hash key depending on tags or not
                hkey = args[0]
                #take care of tagged key names for forcing multiple keys on the same node, e.g. r.set(['userinfo', "age:uid"], value)
                if isinstance(args[0], list):
                    hkey = args[0][0]
                    L = list(args)
                    L[0] = args[0][1]
                    args = tuple(L)

                #get the node number
                node = self._getnodenamefor(hkey)
                redisent = self.redises[self.cluster['default_node']]
                if name in StrictRedisCluster._write_keys:
                    redisent = self.redises[node]
                elif name in StrictRedisCluster._read_keys:
                    redisent = self.redises[
                        self.cluster['master_of'][node]]

                #Execute the command on the server
                return getattr(redisent, name)(*args, **kwargs)

            else:
                result = {}
                for alias, redisent in iteritems(self.redises):
                    if name in StrictRedisCluster._write_keys and alias not in self.cluster['master_of']:
                        res = None
                    else:
                        res = getattr(redisent, name)(*args, **kwargs)

                    if name == 'keys':
                        result.append(res)
                    else:
                        result[alias] = res

                return result
Exemplo n.º 23
0
 def subscribe(self, *args, **kwargs):
     """
     Subscribe to channels. Channels supplied as keyword arguments expect
     a channel name as the key and a callable as the value. A channel's
     callable will be invoked automatically when a message is received on
     that channel rather than producing a message via ``listen()`` or
     ``get_message()``.
     """
     if args:
         args = list_or_args(args[0], args[1:])
     new_channels = {}
     new_channels.update(dict.fromkeys(imap(self.encode, args)))
     for channel, handler in iteritems(kwargs):
         new_channels[self.encode(channel)] = handler
     ret_val = self.execute_command('SUBSCRIBE', *iterkeys(new_channels))
     # update the channels dict AFTER we send the command. we don't want to
     # subscribe twice to these channels, once for the command and again
     # for the reconnection.
     self.channels.update(new_channels)
     return ret_val
Exemplo n.º 24
0
    def _connect(self):
        "Create a TCP socket connection"
        # we want to mimic what socket.create_connection does to support
        # ipv4/ipv6, but we want to set options prior to calling
        # socket.connect()
        err = None
        for res in socket.getaddrinfo(self.host, self.port, self.socket_type,
                                      socket.SOCK_STREAM):
            family, socktype, proto, canonname, socket_address = res
            sock = None
            try:
                sock = socket.socket(family, socktype, proto)
                # TCP_NODELAY
                sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

                # TCP_KEEPALIVE
                if self.socket_keepalive:
                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                    for k, v in iteritems(self.socket_keepalive_options):
                        sock.setsockopt(socket.IPPROTO_TCP, k, v)

                # set the socket_connect_timeout before we connect
                sock.settimeout(self.socket_connect_timeout)

                # connect
                sock.connect(socket_address)

                # set the socket_timeout now that we're connected
                sock.settimeout(self.socket_timeout)
                return sock

            except socket.error as _:
                err = _
                if sock is not None:
                    sock.close()

        if err is not None:
            raise err
        raise socket.error("socket.getaddrinfo returned an empty list")
Exemplo n.º 25
0
    def _connect(self):
        "Create a TCP socket connection"
        # we want to mimic what socket.create_connection does to support
        # ipv4/ipv6, but we want to set options prior to calling
        # socket.connect()
        err = None
        for res in socket.getaddrinfo(self.host, self.port, 0,
                                      socket.SOCK_STREAM):
            family, socktype, proto, canonname, socket_address = res
            sock = None
            try:
                sock = socket.socket(family, socktype, proto)
                # TCP_NODELAY
                sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

                # TCP_KEEPALIVE
                if self.socket_keepalive:
                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                    for k, v in iteritems(self.socket_keepalive_options):
                        sock.setsockopt(socket.SOL_TCP, k, v)

                # set the socket_connect_timeout before we connect
                sock.settimeout(self.socket_connect_timeout)

                # connect
                sock.connect(socket_address)

                # set the socket_timeout now that we're connected
                sock.settimeout(self.socket_timeout)
                return sock

            except socket.error as _:
                err = _
                if sock is not None:
                    sock.close()

        if err is not None:
            raise err
        raise socket.error("socket.getaddrinfo returned an empty list")
Exemplo n.º 26
0
    def zaddnx(self, name, *args, **kwargs):
        """Like zadd but don't update the score of existing elements,

        Set any number of score, element-name pairs to the key ``name``. Pairs
        can be specified in two ways:

        As *args, in the form of: score1, name1, score2, name2, ...
        or as **kwargs, in the form of: name1=score1, name2=score2, ...

        The following example would add four values to the 'my-key' key:
        redis.zaddnx('my-key', 1.1, 'name1', 2.2, 'name2', name3=3.3, name4=4.4)
        """
        pieces = []
        if args:
            if len(args) % 2 != 0:
                raise RedisError("ZADDNX requires an equal number of "
                                 "values and scores")
            pieces.extend(args)
        for pair in iteritems(kwargs):
            pieces.append(pair[1])
            pieces.append(pair[0])

        return ZADDNX(keys=[name], args=pieces, client=self)
Exemplo n.º 27
0
    def zadd(self, name, *args, **kwargs):
        """
        Set any number of score, element-name pairs to the key ``name``. Pairs
        can be specified in two ways:

        As *args, in the form of: score1, name1, score2, name2, ...
        or as **kwargs, in the form of: name1=score1, name2=score2, ...

        The following example would add four values to the 'my-key' key:
        redis.zadd('my-key', 1.1, 'name1', 2.2, 'name2', name3=3.3, name4=4.4)

        If using non-strict Redis (strict_redis=False), args are expected in swapped form:
        redis.zadd('my-key', 'name1', 1.1, 'name2', 2.2, name3=3.3, name4=4.4)
        """
        pieces = []
        if args:
            if len(args) % 2 != 0:
                raise RedisError("ZADD requires an equal number of "
                                 "values and scores")
            pieces.extend(self.strict_redis and args or reversed(args))
        for pair in iteritems(kwargs):
            pieces.append(pair[1])
            pieces.append(pair[0])
        return self.execute_command('ZADD', name, *pieces)
Exemplo n.º 28
0
    def from_url(cls, url, db=None, decode_components=False, **kwargs):
        """
        Return a connection pool configured from the given URL.

        For example::

            redis://[:password]@localhost:6379/0
            rediss://[:password]@localhost:6379/0
            unix://[:password]@/path/to/socket.sock?db=0

        Three URL schemes are supported:
            redis:// creates a normal TCP socket connection
            rediss:// creates a SSL wrapped TCP socket connection
            unix:// creates a Unix Domain Socket connection

        There are several ways to specify a database number. The parse function
        will return the first specified option:
            1. A ``db`` querystring option, e.g. redis://localhost?db=0
            2. If using the redis:// scheme, the path argument of the url, e.g.
               redis://localhost/0
            3. The ``db`` argument to this function.

        If none of these options are specified, db=0 is used.

        The ``decode_components`` argument allows this function to work with
        percent-encoded URLs. If this argument is set to ``True`` all ``%xx``
        escapes will be replaced by their single-character equivalents after
        the URL has been parsed. This only applies to the ``hostname``,
        ``path``, and ``password`` components.

        Any additional querystring arguments and keyword arguments will be
        passed along to the ConnectionPool class's initializer. The querystring
        arguments ``socket_connect_timeout`` and ``socket_timeout`` if supplied
        are parsed as float values. The arguments ``socket_keepalive`` and
        ``retry_on_timeout`` are parsed to boolean values that accept
        True/False, Yes/No values to indicate state. Invalid types cause a
        ``UserWarning`` to be raised. In the case of conflicting arguments,
        querystring arguments always win.
        """
        url_string = url
        url = urlparse(url)
        qs = ''

        # in python2.6, custom URL schemes don't recognize querystring values
        # they're left as part of the url.path.
        if '?' in url.path and not url.query:
            # chop the querystring including the ? off the end of the url
            # and reparse it.
            qs = url.path.split('?', 1)[1]
            url = urlparse(url_string[:-(len(qs) + 1)])
        else:
            qs = url.query

        url_options = {}

        for name, value in iteritems(parse_qs(qs)):
            if value and len(value) > 0:
                parser = URL_QUERY_ARGUMENT_PARSERS.get(name)
                if parser:
                    try:
                        url_options[name] = parser(value[0])
                    except (TypeError, ValueError):
                        warnings.warn(UserWarning(
                            "Invalid value for `%s` in connection URL." % name
                        ))
                else:
                    url_options[name] = value[0]

        if decode_components:
            password = unquote(url.password) if url.password else None
            path = unquote(url.path) if url.path else None
            hostname = unquote(url.hostname) if url.hostname else None
        else:
            password = url.password
            path = url.path
            hostname = url.hostname

        # We only support redis:// and unix:// schemes.
        if url.scheme == 'unix':
            url_options.update({
                'password': password,
                'path': path,
                'connection_class': UnixDomainSocketConnection,
            })

        else:
            url_options.update({
                'host': hostname,
                'port': int(url.port or 6379),
                'password': password,
            })

            # If there's a path argument, use it as the db argument if a
            # querystring value wasn't specified
            if 'db' not in url_options and path:
                try:
                    url_options['db'] = int(path.replace('/', ''))
                except (AttributeError, ValueError):
                    pass

            if url.scheme == 'rediss':
                url_options['connection_class'] = SSLConnection

        # last shot at the db value
        url_options['db'] = int(url_options.get('db', db or 0))

        # update the arguments from the URL values
        kwargs.update(url_options)

        # backwards compatability
        if 'charset' in kwargs:
            warnings.warn(DeprecationWarning(
                '"charset" is deprecated. Use "encoding" instead'))
            kwargs['encoding'] = kwargs.pop('charset')
        if 'errors' in kwargs:
            warnings.warn(DeprecationWarning(
                '"errors" is deprecated. Use "encoding_errors" instead'))
            kwargs['encoding_errors'] = kwargs.pop('errors')

        return cls(**kwargs)
Exemplo n.º 29
0
    def from_url(cls, url, db=None, **kwargs):
        """
        Return a connection pool configured from the given URL.

        For example::

            redis://[:password]@localhost:6379/0
            rediss://[:password]@localhost:6379/0
            unix://[:password]@/path/to/socket.sock?db=0

        Three URL schemes are supported:
            redis:// creates a normal TCP socket connection
            rediss:// creates a SSL wrapped TCP socket connection
            unix:// creates a Unix Domain Socket connection

        There are several ways to specify a database number. The parse function
        will return the first specified option:
            1. A ``db`` querystring option, e.g. redis://localhost?db=0
            2. If using the redis:// scheme, the path argument of the url, e.g.
               redis://localhost/0
            3. The ``db`` argument to this function.

        If none of these options are specified, db=0 is used.

        Any additional querystring arguments and keyword arguments will be
        passed along to the ConnectionPool class's initializer. In the case
        of conflicting arguments, querystring arguments always win.
        """
        url_string = url
        url = urlparse(url)
        qs = ''

        # in python2.6, custom URL schemes don't recognize querystring values
        # they're left as part of the url.path.
        if '?' in url.path and not url.query:
            # chop the querystring including the ? off the end of the url
            # and reparse it.
            qs = url.path.split('?', 1)[1]
            url = urlparse(url_string[:-(len(qs) + 1)])
        else:
            qs = url.query

        url_options = {}

        for name, value in iteritems(parse_qs(qs)):
            if value and len(value) > 0:
                url_options[name] = value[0]

        # We only support redis:// and unix:// schemes.
        if url.scheme == 'unix':
            url_options.update({
                'password': url.password,
                'path': url.path,
                'connection_class': UnixDomainSocketConnection,
            })

        else:
            url_options.update({
                'host': url.hostname,
                'port': int(url.port or 6379),
                'password': url.password,
            })

            # If there's a path argument, use it as the db argument if a
            # querystring value wasn't specified
            if 'db' not in url_options and url.path:
                try:
                    url_options['db'] = int(url.path.replace('/', ''))
                except (AttributeError, ValueError):
                    pass

            if url.scheme == 'rediss':
                url_options['connection_class'] = SSLConnection

        # last shot at the db value
        url_options['db'] = int(url_options.get('db', db or 0))

        # update the arguments from the URL values
        kwargs.update(url_options)

        # backwards compatability
        if 'charset' in kwargs:
            warnings.warn(
                DeprecationWarning(
                    '"charset" is deprecated. Use "encoding" instead'))
            kwargs['encoding'] = kwargs.pop('charset')
        if 'errors' in kwargs:
            warnings.warn(
                DeprecationWarning(
                    '"errors" is deprecated. Use "encoding_errors" instead'))
            kwargs['encoding_errors'] = kwargs.pop('errors')

        return cls(**kwargs)
Exemplo n.º 30
0
    def from_url(cls, url, db=None, **kwargs):
        """
        Return a connection pool configured from the given URL.

        For example::

            redis://[:password]@localhost:6379/0
            rediss://[:password]@localhost:6379/0
            unix://[:password]@/path/to/socket.sock?db=0

        Three URL schemes are supported:
            redis:// creates a normal TCP socket connection
            rediss:// creates a SSL wrapped TCP socket connection
            unix:// creates a Unix Domain Socket connection

        There are several ways to specify a database number. The parse function
        will return the first specified option:
            1. A ``db`` querystring option, e.g. redis://localhost?db=0
            2. If using the redis:// scheme, the path argument of the url, e.g.
               redis://localhost/0
            3. The ``db`` argument to this function.

        If none of these options are specified, db=0 is used.

        Any additional querystring arguments and keyword arguments will be
        passed along to the ConnectionPool class's initializer. In the case
        of conflicting arguments, querystring arguments always win.
        """
        url_string = url
        url = urlparse(url)
        qs = ''

        # in python2.6, custom URL schemes don't recognize querystring values
        # they're left as part of the url.path.
        if '?' in url.path and not url.query:
            # chop the querystring including the ? off the end of the url
            # and reparse it.
            qs = url.path.split('?', 1)[1]
            url = urlparse(url_string[:-(len(qs) + 1)])
        else:
            qs = url.query

        url_options = {}

        for name, value in iteritems(parse_qs(qs)):
            if value and len(value) > 0:
                url_options[name] = value[0]

        # We only support redis:// and unix:// schemes.
        if url.scheme == 'unix':
            url_options.update({
                'password': url.password,
                'path': url.path,
                'connection_class': UnixDomainSocketConnection,
            })

        else:
            url_options.update({
                'host': url.hostname,
                'port': int(url.port or 6379),
                'password': url.password,
            })

            # If there's a path argument, use it as the db argument if a
            # querystring value wasn't specified
            if 'db' not in url_options and url.path:
                try:
                    url_options['db'] = int(url.path.replace('/', ''))
                except (AttributeError, ValueError):
                    pass

            if url.scheme == 'rediss':
                url_options['connection_class'] = SSLConnection

        # last shot at the db value
        url_options['db'] = int(url_options.get('db', db or 0))

        # update the arguments from the URL values
        kwargs.update(url_options)

        # backwards compatability
        if 'charset' in kwargs:
            warnings.warn(DeprecationWarning(
                '"charset" is deprecated. Use "encoding" instead'))
            kwargs['encoding'] = kwargs.pop('charset')
        if 'errors' in kwargs:
            warnings.warn(DeprecationWarning(
                '"errors" is deprecated. Use "encoding_errors" instead'))
            kwargs['encoding_errors'] = kwargs.pop('errors')

        return cls(**kwargs)
Exemplo n.º 31
0
        def function(*args, **kwargs):
            if name not in StrictRedisCluster._loop_keys:
                # take care of hash tags
                tag_start = None
                key_type = hash_tag = ''
                # since we don't have "first item" in dict,
                # this list is needed in order to check hash_tag in mset({"a{a}": "a", "b":"b"})
                list_ht = []
                if isinstance(args[0], (basestring, bytes)):
                    key_type = 'string'
                    list_ht.append(args[0])
                else:
                    if isinstance(args[0], list):
                        key_type = 'list'
                        list_ht.append(args[0][0])
                    else:
                        key_type = 'dict'
                        list_ht = iterkeys(args[0])

                # check for hash tags
                for k in list_ht:
                    try:
                        tag_start = k.index('{')
                        hash_tag = k
                        break
                    except Exception as e:
                        tag_start = None

                # trigger error msg on tag keys unless we have hash tags e.g. "bar{zap}"
                if name in StrictRedisCluster._tag_keys and not tag_start:
                    try:
                        return getattr(self, '_rc_' + name)(*args, **kwargs)
                    except AttributeError:
                        raise redis.DataError("rediscluster: Command %s Not Supported (each key name has its own node)" % name)

                # get the hash key
                hkey = args[0]
                # take care of hash tags names for forcing multiple keys on the same node,
                # e.g. r.set("bar{zap}", "bar"), r.mget(["foo{foo}","bar"])
                if tag_start is not None:
                    L = list(args)
                    if key_type != 'string':
                        if key_type == 'list':
                            hkey = L[0][0][tag_start + 1:-1]
                            L[0][0] = L[0][0][0:tag_start]
                        else:
                            hkey = hash_tag[tag_start + 1:-1]
                            L[0][hash_tag[0:tag_start]] = L[0][hash_tag]
                            del L[0][hash_tag]
                    else:
                        hkey = L[0][tag_start + 1:-1]
                        L[0] = L[0][0:tag_start]

                    args = tuple(L)

                # get the node number
                node = self.getnodenamefor(hkey)
                if name in StrictRedisCluster._write_keys:
                    redisent = self.redises[node]
                elif name in StrictRedisCluster._read_keys:
                    redisent = self.redises[node + '_slave']
                else:
                    raise redis.DataError("rediscluster: Command %s Not Supported (each key name has its own node)" % name)

                # Execute the command on the server
                return getattr(redisent, name)(*args, **kwargs)

            else:

                # take care of keys that don't need to go through master and slaves redis servers
                if name not in self._loop_keys_admin:
                    try:
                        return getattr(self, '_rc_' + name)(*args, **kwargs)
                    except AttributeError:
                        raise redis.DataError("rediscluster: Command %s Not Supported (each key name has its own node)" % name)

                result = {}
                for alias, redisent in iteritems(self.redises):
                    if (name in StrictRedisCluster._write_keys and alias.find('_slave') >= 0) or (name in StrictRedisCluster._read_keys and alias.find('_slave') == -1):
                        res = None
                    else:
                        res = getattr(redisent, name)(*args, **kwargs)

                    result[alias] = res

                return result
Exemplo n.º 32
0
    def __init__(self, cluster={}, db=0, mastersonly=False):
        # raise exception when wrong server hash
        if 'nodes' not in cluster:
            raise Exception(
                "rediscluster: Please set a correct array of redis cluster.")

        self.cluster = cluster
        have_master_of = 'master_of' in self.cluster
        self.no_servers = len(self.cluster['master_of']) if have_master_of else len(self.cluster['nodes'])

        self.redises = {}
        redises_cons = {}
        self.cluster['slaves'] = {}

        # connect to all servers
        for alias, server in iteritems(self.cluster['nodes']):

            if have_master_of and alias not in self.cluster['master_of']:
                continue

            server_str = str(server)
            if server_str in redises_cons:
                self.redises[alias] = redises_cons[server_str]['master']
                self.redises[alias +
                             '_slave'] = redises_cons[server_str]['slave']
                self.cluster['slaves'][alias +
                                       '_slave'] = redises_cons[server_str]['slave_node']
            else:
                try:
                    # connect to master
                    self.__redis = redis.StrictRedis(db=db, **server)
                    if not mastersonly and not have_master_of:
                        info = self.__redis.info()
                        if info['role'] != 'master':
                            raise redis.DataError(
                                "rediscluster: server %s is not a master." % (server,))

                    self.redises[alias] = self.__redis
                    redises_cons[server_str] = {}
                    redises_cons[server_str]['master'] = self.redises[alias]

                    # connect to slave
                    slave_connected = False
                    slave = {}
                    if not mastersonly:
                        if have_master_of:
                            slave = self.cluster[
                                'nodes'][self.cluster['master_of'][alias]]
                        elif 'connected_slaves' in info and info['connected_slaves'] > 0:
                            slave_host, slave_port, slave_online = info[
                                'slave0'].split(',')
                            if slave_online == 'online':
                                slave = {'host': slave_host, 'port': slave_port}

                    if slave :
                        try:
                            redis_slave = redis.StrictRedis(host=slave['host'], port=int(slave['port']), db=db)
                            self.redises[alias + '_slave'] = redis_slave
                            self.cluster['slaves'][alias + '_slave'] = {
                                'host': slave['host'], 'port': slave['port']}
                            redises_cons[server_str][
                                'slave'] = self.redises[alias + '_slave']
                            redises_cons[server_str]['slave_node'] = self.cluster['slaves'][alias + '_slave']
                            slave_connected = True
                        except redis.RedisError as e:
                            pass
                            # "RedisCluster cannot connect to: " + slave_host +':'+ slave_port

                    if not slave_connected:
                        self.redises[alias + '_slave'] = self.redises[alias]
                        self.cluster['slaves'][alias + '_slave'] = server
                        redises_cons[server_str][
                            'slave'] = self.redises[alias + '_slave']
                        redises_cons[server_str]['slave_node'] = self.cluster[
                            'slaves'][alias + '_slave']

                except redis.RedisError as e:
                    raise redis.ConnectionError(
                        "rediscluster cannot connect to: %s %s" % (server, e))
Exemplo n.º 33
0
    def __init__(self, cluster={}, db=0, mastersonly=False):
        # raise exception when wrong server hash
        if 'nodes' not in cluster:
            raise Exception(
                "rediscluster: Please set a correct array of redis cluster.")

        self.cluster = cluster
        have_master_of = 'master_of' in self.cluster
        self.no_servers = len(
            self.cluster['master_of']) if have_master_of else len(
                self.cluster['nodes'])

        self.redises = {}
        redises_cons = {}
        self.cluster['slaves'] = {}

        # connect to all servers
        for alias, server in iteritems(self.cluster['nodes']):

            if have_master_of and alias not in self.cluster['master_of']:
                continue

            server_str = str(server)
            if server_str in redises_cons:
                self.redises[alias] = redises_cons[server_str]['master']
                self.redises[alias +
                             '_slave'] = redises_cons[server_str]['slave']
                self.cluster['slaves'][
                    alias + '_slave'] = redises_cons[server_str]['slave_node']
            else:
                try:
                    # connect to master
                    self.__redis = redis.StrictRedis(db=db, **server)
                    if not mastersonly and not have_master_of:
                        info = self.__redis.info()
                        if info['role'] != 'master':
                            raise redis.DataError(
                                "rediscluster: server %s is not a master." %
                                (server, ))

                    self.redises[alias] = self.__redis
                    redises_cons[server_str] = {}
                    redises_cons[server_str]['master'] = self.redises[alias]

                    # connect to slave
                    slave_connected = False
                    slave = {}
                    if not mastersonly:
                        if have_master_of:
                            slave = self.cluster['nodes'][
                                self.cluster['master_of'][alias]]
                        elif 'connected_slaves' in info and info[
                                'connected_slaves'] > 0:
                            slave_host, slave_port, slave_online = info[
                                'slave0'].split(',')
                            if slave_online == 'online':
                                slave = {
                                    'host': slave_host,
                                    'port': slave_port
                                }

                    if slave:
                        try:
                            redis_slave = redis.StrictRedis(host=slave['host'],
                                                            port=int(
                                                                slave['port']),
                                                            db=db)
                            self.redises[alias + '_slave'] = redis_slave
                            self.cluster['slaves'][alias + '_slave'] = {
                                'host': slave['host'],
                                'port': slave['port']
                            }
                            redises_cons[server_str]['slave'] = self.redises[
                                alias + '_slave']
                            redises_cons[server_str][
                                'slave_node'] = self.cluster['slaves'][
                                    alias + '_slave']
                            slave_connected = True
                        except redis.RedisError as e:
                            pass
                            # "RedisCluster cannot connect to: " + slave_host +':'+ slave_port

                    if not slave_connected:
                        self.redises[alias + '_slave'] = self.redises[alias]
                        self.cluster['slaves'][alias + '_slave'] = server
                        redises_cons[server_str]['slave'] = self.redises[
                            alias + '_slave']
                        redises_cons[server_str]['slave_node'] = self.cluster[
                            'slaves'][alias + '_slave']

                except redis.RedisError as e:
                    raise redis.ConnectionError(
                        "rediscluster cannot connect to: %s %s" % (server, e))
Exemplo n.º 34
0
 def test_mset_kwargs(self, r):
     d = {'a': b('1'), 'b': b('2'), 'c': b('3')}
     assert r.mset(**d)
     for k, v in iteritems(d):
         assert r[k] == v
Exemplo n.º 35
0
        def function(*args, **kwargs):
            if name not in StrictRedisCluster._loop_keys:
                # take care of hash tags
                tag_start = None
                key_type = hash_tag = ''
                # since we don't have "first item" in dict,
                # this list is needed in order to check hash_tag in mset({"a{a}": "a", "b":"b"})
                list_ht = []
                if isinstance(args[0], (basestring, bytes)):
                    key_type = 'string'
                    list_ht.append(args[0])
                else:
                    if isinstance(args[0], list):
                        key_type = 'list'
                        list_ht.append(args[0][0])
                    else:
                        key_type = 'dict'
                        list_ht = dictkeys(args[0])

                # check for hash tags
                for k in list_ht:
                    try:
                        tag_start = k.index('{')
                        hash_tag = k
                        break
                    except Exception as e:
                        tag_start = None

                # trigger error msg on tag keys unless we have hash tags e.g. "bar{zap}"
                if name in StrictRedisCluster._tag_keys and not tag_start:
                    try:
                        return getattr(self, '_rc_' + name)(*args, **kwargs)
                    except AttributeError:
                        raise redis.DataError(
                            "rediscluster: Command %s Not Supported (each key name has its own node)"
                            % name)

                # get the hash key
                hkey = args[0]
                # take care of hash tags names for forcing multiple keys on the same node,
                # e.g. r.set("bar{zap}", "bar"), r.mget(["foo{foo}","bar"])
                if tag_start is not None:
                    L = list(args)
                    if key_type != 'string':
                        if key_type == 'list':
                            hkey = L[0][0][tag_start + 1:-1]
                            L[0][0] = L[0][0][0:tag_start]
                        else:
                            hkey = hash_tag[tag_start + 1:-1]
                            L[0][hash_tag[0:tag_start]] = L[0][hash_tag]
                            del L[0][hash_tag]
                    else:
                        hkey = L[0][tag_start + 1:-1]
                        L[0] = L[0][0:tag_start]

                    args = tuple(L)

                # get the node number
                node = self._getnodenamefor(hkey)
                if name in StrictRedisCluster._write_keys:
                    redisent = self.redises[node]
                elif name in StrictRedisCluster._read_keys:
                    redisent = self.redises[node + '_slave']
                else:
                    raise redis.DataError(
                        "rediscluster: Command %s Not Supported (each key name has its own node)"
                        % name)

                # Execute the command on the server
                return getattr(redisent, name)(*args, **kwargs)

            else:

                # take care of keys that don't need to go through master and slaves redis servers
                if name not in self._loop_keys_admin:
                    try:
                        return getattr(self, '_rc_' + name)(*args, **kwargs)
                    except AttributeError:
                        raise redis.DataError(
                            "rediscluster: Command %s Not Supported (each key name has its own node)"
                            % name)

                result = {}
                for alias, redisent in iteritems(self.redises):
                    if (name in StrictRedisCluster._write_keys
                            and alias.find('_slave') >= 0) or (
                                name in StrictRedisCluster._read_keys
                                and alias.find('_slave') == -1):
                        res = None
                    else:
                        res = getattr(redisent, name)(*args, **kwargs)

                    result[alias] = res

                return result
Exemplo n.º 36
0
 def _rc_mset(self, mapping):
     "Sets each key in the ``mapping`` dict to its corresponding value"
     result = True
     for k, v in iteritems(mapping):
         result = result and self.set(k, v)
     return result
Exemplo n.º 37
0
 def test_mset(self, r):
     d = {'a': b'1', 'b': b'2', 'c': b'3'}
     assert r.mset(d)
     for k, v in iteritems(d):
         assert r[k] == v
Exemplo n.º 38
0
    def from_url(cls, url, db=None, decode_components=False, **kwargs):
        """
        Return a connection pool configured from the given URL.

        For example::

            redis://[:password]@localhost:6379/0
            rediss://[:password]@localhost:6379/0
            unix://[:password]@/path/to/socket.sock?db=0

        Three URL schemes are supported:

        - ```redis://``
          <https://www.iana.org/assignments/uri-schemes/prov/redis>`_ creates a
          normal TCP socket connection
        - ```rediss://``
          <https://www.iana.org/assignments/uri-schemes/prov/rediss>`_ creates
          a SSL wrapped TCP socket connection
        - ``unix://`` creates a Unix Domain Socket connection

        There are several ways to specify a database number. The parse function
        will return the first specified option:
            1. A ``db`` querystring option, e.g. redis://localhost?db=0
            2. If using the redis:// scheme, the path argument of the url, e.g.
               redis://localhost/0
            3. The ``db`` argument to this function.

        If none of these options are specified, db=0 is used.

        The ``decode_components`` argument allows this function to work with
        percent-encoded URLs. If this argument is set to ``True`` all ``%xx``
        escapes will be replaced by their single-character equivalents after
        the URL has been parsed. This only applies to the ``hostname``,
        ``path``, and ``password`` components.

        Any additional querystring arguments and keyword arguments will be
        passed along to the ConnectionPool class's initializer. The querystring
        arguments ``socket_connect_timeout`` and ``socket_timeout`` if supplied
        are parsed as float values. The arguments ``socket_keepalive`` and
        ``retry_on_timeout`` are parsed to boolean values that accept
        True/False, Yes/No values to indicate state. Invalid types cause a
        ``UserWarning`` to be raised. In the case of conflicting arguments,
        querystring arguments always win.

        """
        url = urlparse(url)
        url_options = {}

        for name, value in iteritems(parse_qs(url.query)):
            if value and len(value) > 0:
                parser = URL_QUERY_ARGUMENT_PARSERS.get(name)
                if parser:
                    try:
                        url_options[name] = parser(value[0])
                    except (TypeError, ValueError):
                        warnings.warn(
                            UserWarning(
                                "Invalid value for `%s` in connection URL." %
                                name))
                else:
                    url_options[name] = value[0]

        if decode_components:
            password = unquote(url.password) if url.password else None
            path = unquote(url.path) if url.path else None
            hostname = unquote(url.hostname) if url.hostname else None
        else:
            password = url.password
            path = url.path
            hostname = url.hostname

        # We only support redis://, rediss:// and unix:// schemes.
        if url.scheme == 'unix':
            url_options.update({
                'password': password,
                'path': path,
                'connection_class': UnixDomainSocketConnection,
            })

        elif url.scheme in ('redis', 'rediss'):
            url_options.update({
                'host': hostname,
                'port': int(url.port or 6379),
                'password': password,
            })

            # If there's a path argument, use it as the db argument if a
            # querystring value wasn't specified
            if 'db' not in url_options and path:
                try:
                    url_options['db'] = int(path.replace('/', ''))
                except (AttributeError, ValueError):
                    pass

            if url.scheme == 'rediss':
                url_options['connection_class'] = SSLConnection
        else:
            valid_schemes = ', '.join(('redis://', 'rediss://', 'unix://'))
            raise ValueError('Redis URL must specify one of the following'
                             'schemes (%s)' % valid_schemes)

        # last shot at the db value
        url_options['db'] = int(url_options.get('db', db or 0))

        # update the arguments from the URL values
        kwargs.update(url_options)

        # backwards compatability
        if 'charset' in kwargs:
            warnings.warn(
                DeprecationWarning(
                    '"charset" is deprecated. Use "encoding" instead'))
            kwargs['encoding'] = kwargs.pop('charset')
        if 'errors' in kwargs:
            warnings.warn(
                DeprecationWarning(
                    '"errors" is deprecated. Use "encoding_errors" instead'))
            kwargs['encoding_errors'] = kwargs.pop('errors')

        return cls(**kwargs)
Exemplo n.º 39
0
    def _connect(self):
        "Create a TCP socket connection"
        # we want to mimic what socket.create_connection does to support
        # ipv4/ipv6, but we want to set options prior to calling
        # socket.connect()
        if self._iostream:
            return

        current = self._get_current_greenlet()
        parent = current.parent

        err = None
        for res in socket.getaddrinfo(self.host, self.port, 0,
                                      socket.SOCK_STREAM):
            family, socktype, proto, _, socket_address = res
            sock = None
            try:
                sock = socket.socket(family, socktype, proto)
                # TCP_NODELAY
                sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

                # TCP_KEEPALIVE
                if self.socket_keepalive:
                    sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)
                    for k, v in iteritems(self.socket_keepalive_options):
                        sock.setsockopt(socket.SOL_TCP, k, v)

                self._iostream = self._wrap_socket(sock)

                timeout = self.socket_connect_timeout
                if timeout:
                    self._timeout_handle = self._ioloop.add_timeout(
                        datetime.timedelta(seconds=timeout),
                        functools.partial(self._handle_timeout, current),
                    )

                handle = generate_handle()
                self._events[handle] = True
                self._iostream.set_close_callback(
                    functools.partial(self._handle_error, current, handle)
                )
                try:
                    self._iostream.connect(
                        socket_address,
                        callback=functools.partial(self._handle_connect, current, handle)
                    )
                    # yield back to parent, wait for connect, error or timeout
                    parent.switch()
                finally:
                    if self._timeout_handle:
                        self._ioloop.remove_timeout(self._timeout_handle)
                        self._timeout_handle = None
                    self._iostream.set_close_callback(None)
                    del self._events[handle]
                return sock
            except ConnectionError as _:
                err = _
                if sock is not None:
                    sock.close()
                if self._iostream is not None:
                    self._iostream.close()
                    self._iostream = None

        if err is not None:
            raise err
        raise socket.error("socket.getaddrinfo returned an empty list")
Exemplo n.º 40
0
 def test_mset_kwargs(self, r):
     d = {'a': b('1'), 'b': b('2'), 'c': b('3')}
     assert r.mset(**d)
     for k, v in iteritems(d):
         assert r[k] == v
Exemplo n.º 41
0
 def _rc_mset(self, mapping):
     "Sets each key in the ``mapping`` dict to its corresponding value"
     result = True
     for k, v in iteritems(mapping):
         result = result and self.set(k, v)
     return result
Exemplo n.º 42
0
 def __init__(self, data):
     for k, v in iteritems(data):
         self[k.upper()] = v
Exemplo n.º 43
0
    def from_url(cls, url, db=None, **kwargs):
        """
        Return a connection pool configured from the given URL.

        For example::

            redis://[:password]@localhost:6379/0
            unix://[:password]@/path/to/socket.sock?db=0

        There are several ways to specify a database number. The parse function
        will return the first specified option:
            1. A ``db`` querystring option, e.g. redis://localhost?db=0
            2. If using the redis:// scheme, the path argument of the url, e.g.
               redis://localhost/0
            3. The ``db`` argument to this function.

        If none of these options are specified, db=0 is used.

        Any additional querystring arguments and keyword arguments will be
        passed along to the ConnectionPool class's initializer. In the case
        of conflicting arguments, querystring arguments always win.
        """
        # in python2.6, custom URL schemes don't recognize querystring values
        # split the url manually instead
        pieces = url.split('?', 1)
        url, qs = '', ''
        if len(pieces) == 2:
            url, qs = pieces
        else:
            url = pieces[0]

        url = urlparse(url)
        url_options = {}

        for name, value in iteritems(parse_qs(qs)):
            if value and len(value) > 0:
                url_options[name] = value[0]

        # We only support redis:// and unix:// schemes.
        if url.scheme == 'unix':
            url_options.update({
                'password': url.password,
                'path': url.path,
                'connection_class': UnixDomainSocketConnection,
            })

        else:
            url_options.update({
                'host': url.hostname,
                'port': int(url.port or 6379),
                'password': url.password,
            })

            # If there's a path argument, use it as the db argument if a
            # querystring value wasn't specified
            if 'db' not in url_options and url.path:
                try:
                    url_options['db'] = int(url.path.replace('/', ''))
                except (AttributeError, ValueError):
                    pass

        # last shot at the db value
        url_options['db'] = int(url_options.get('db', db or 0))

        # update the arguments from the URL values
        kwargs.update(url_options)
        return cls(**kwargs)