def psubscribe(self, *context, **delivery_mechanisms):
        """
        Subscribe to channel patterns. Patterns are specified as keyword arguments.
        The pattern string is the key and the value is either a callable, or
        a Queue.Queue. If a callable, then it will be invoked when a message arrives
        that pattern. The argument will be a BusMessage. If a keyword argument's
        value is a Queue.Queue instance, then the BusMessage that encapsulates an
        incoming message will be placed on the queue.
        
        :param *context: an optional data structure that will be included
            in the BusMessage object that is passed to the callable(s) when
            messages arrive. The context will be available in the BusMessage
            as property 'context'. Used, for instance, to pass an Event object
            that the callable sets when it is done.
        :type *context: <any>
        :param *delivery_mechanisms-keys: keys of keyword arguments are channel patterns
            to subscribe to.
        :type *delivery_mechanisms-keys: string
        :param *delivery_mechanisms-values: either a callable or a Queue.Queue instance
        :type *delivery_mechanisms-values: {callable | Queue.Queue}
        :returns: the number of channels the caller is now subscribed to.
        :rtype: int
        :raises TimeoutError: when server does not respond in time.
        """

        if self.connection is None:
            self.connection = self.connection_pool.get_connection()

        if context:
            context = context[0]
        else:
            context = None
        new_patterns = {}
        for pattern, handler_or_queue in iteritems(delivery_mechanisms):
            # Map channel to a tuple containing the handler or queue and
            # the context for use in handle_message():
            new_patterns[self.encode(pattern)] = (handler_or_queue, context)

        # Get the access lock to the dict that
        # holds the pattern-subscriptions:
        self.patterns.acquire()

        try:
            ret_val = self.execute_command('PSUBSCRIBE',
                                           *iterkeys(new_patterns))

            # Now update the self.patterns data structure;
            # acquire the access lock.
            # (update the patternss dict AFTER we send the command. we don't want to
            # subscribe twice to these patterns, once for the command and again
            # for the reconnection.):

            self.patterns.update(new_patterns)
        finally:
            self.patterns.release()

        return ret_val
    def psubscribe(self, *context, **delivery_mechanisms):
        """
        Subscribe to channel patterns. Patterns are specified as keyword arguments.
        The pattern string is the key and the value is either a callable, or
        a Queue.Queue. If a callable, then it will be invoked when a message arrives
        that pattern. The argument will be a BusMessage. If a keyword argument's
        value is a Queue.Queue instance, then the BusMessage that encapsulates an
        incoming message will be placed on the queue.
        
        :param *context: an optional data structure that will be included
            in the BusMessage object that is passed to the callable(s) when
            messages arrive. The context will be available in the BusMessage
            as property 'context'. Used, for instance, to pass an Event object
            that the callable sets when it is done.
        :type *context: <any>
        :param *delivery_mechanisms-keys: keys of keyword arguments are channel patterns
            to subscribe to.
        :type *delivery_mechanisms-keys: string
        :param *delivery_mechanisms-values: either a callable or a Queue.Queue instance
        :type *delivery_mechanisms-values: {callable | Queue.Queue}
        :returns: the number of channels the caller is now subscribed to.
        :rtype: int
        :raises TimeoutError: when server does not respond in time.
        """
        
        if self.connection is None:
            self.connection = self.connection_pool.get_connection()
        
        if context:
            context = context[0]
        else:
            context = None
        new_patterns = {}
        for pattern, handler_or_queue in iteritems(delivery_mechanisms):
            # Map channel to a tuple containing the handler or queue and
            # the context for use in handle_message():
            new_patterns[self.encode(pattern)] = (handler_or_queue, context)

        # Get the access lock to the dict that
        # holds the pattern-subscriptions:
        self.patterns.acquire()

        try:
            ret_val = self.execute_command('PSUBSCRIBE', *iterkeys(new_patterns))
    
            # Now update the self.patterns data structure;
            # acquire the access lock.
            # (update the patternss dict AFTER we send the command. we don't want to
            # subscribe twice to these patterns, once for the command and again
            # for the reconnection.):
            
            self.patterns.update(new_patterns)
        finally:
            self.patterns.release()

        return ret_val
 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)
 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)
Example #5
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  #@UnusedVariable
            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")
Example #6
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. 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]

        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)