Example #1
0
    def hydrate(cls, db, limit):
        """
        Given a limit dict, as generated by dehydrate(), generate an
        appropriate instance of Limit (or a subclass).  If the
        required limit class cannot be found, returns None.
        """

        # Extract the limit name from the keyword arguments
        cls_name = limit.pop('limit_class')

        # Is it in the registry yet?
        if cls_name not in cls._registry:
            try:
                utils.import_class(cls_name)
            except ImportError:
                # If we failed to import, ignore...
                pass

        # Look it up in the registry
        cls = cls._registry.get(cls_name)

        # Instantiate the thing
        return cls(db, **limit) if cls else None
Example #2
0
def turnstile_filter(global_conf, **local_conf):
    """
    Factory function for turnstile.

    Returns a function which, when passed the application, returns an
    instance of the TurnstileMiddleware.
    """

    # Select the appropriate middleware class to return
    klass = TurnstileMiddleware
    if 'turnstile' in local_conf:
        klass = utils.import_class(local_conf['turnstile'])

    def wrapper(app):
        return klass(app, local_conf)

    return wrapper
Example #3
0
    def __init__(self, app, local_conf):
        """
        Initialize the turnstile middleware.  Saves the configuration
        and sets up the list of preprocessors, connects to the
        database, and initiates the control daemon thread.
        """

        # Save the application
        self.app = app
        self.limits = []
        self.mapper = None

        # Split up the configuration into groups of related variables
        self.config = {
            None: {
                'status': '413 Request Entity Too Large',
                },
            }
        for key, value in local_conf.items():
            outer, _sep, inner = key.partition('.')

            # Deal with prefix-less keys
            if not inner:
                outer, inner = None, outer

            # Make sure we have a place to put them
            self.config.setdefault(outer, {})
            self.config[outer][inner] = value

        # Set up request preprocessors
        self.preprocessors = []
        for preproc in self.config[None].get('preprocess', '').split():
            # Allow ImportError to bubble up
            self.preprocessors.append(utils.import_class(preproc))

        # Next, let's configure redis
        redis_args = self.config.get('redis', {})
        self.db = database.initialize(redis_args)

        # And start up the control daemon
        control_args = self.config.get('control', {})
        self.control_daemon = database.ControlDaemon(self.db, self,
                                                     control_args)
Example #4
0
def parse_limit_node(db, idx, limit):
    """
    Given an XML node describing a limit, return a Limit object.

    :param db: Handle for the Redis database.
    :param idx: The index of the limit in the XML file; used for error
                reporting.
    :param limit: The XML node describing the limit.
    """

    # First, try to import the class; this will raise ImportError if
    # we can't import it
    klass = utils.import_class(limit.get('class'))

    # Build the list of required attributes
    required = set(k for k, v in klass.attrs.items()
                   if 'default' not in v)

    # Now, use introspection on the class to interpret the attributes
    attrs = {}
    for child in limit:
        # Basic validation of child elements
        if child.tag != 'attr':
            warnings.warn("Unrecognized element %r while parsing limit at "
                          "index %d; ignoring..." % (child.tag, idx))
            continue

        # Get the attribute name
        attr = child.get('name')

        # Be liberal in what we accept--ignore unrecognized attributes
        # (with a warning)
        if attr not in klass.attrs:
            warnings.warn("Limit at index %d does not accept an attribute "
                          "%r; ignoring..." % (idx, attr))
            continue

        # OK, get the attribute descriptor
        desc = klass.attrs[attr]

        # Grab the attribute type
        attr_type = desc.get('type', str)

        if attr_type == list:
            # Lists are expressed as child elements; we ignore the
            # child element names
            subtype = desc.get('subtype', str)
            value = []
            for grandchild in child:
                if grandchild.tag != 'value':
                    warnings.warn("Unrecognized element %r while parsing "
                                  "%r attribute of limit at index %d; "
                                  "ignoring..." %
                                  (grandchild.tag, attr, idx))
                    continue

                value.append(subtype(grandchild.text))
        elif attr_type == dict:
            # Dicts are expressed as child elements, with the tags
            # identifying the attribute name
            subtype = desc.get('subtype', str)
            value = {}
            for grandchild in child:
                if grandchild.tag != 'value':
                    warnings.warn("Unrecognized element %r while parsing "
                                  "%r attribute of limit at index %d; "
                                  "ignoring..." %
                                  (grandchild.tag, attr, idx))
                    continue
                elif 'key' not in grandchild.attrib:
                    warnings.warn("Missing 'key' attribute of 'value' "
                                  "element while parsing %r attribute of "
                                  "limit at index %d; ignoring..." %
                                  (attr, idx))
                    continue

                value[grandchild.get('key')] = subtype(grandchild.text)
        else:
            # Simple type conversion
            value = attr_type(child.text)

        # Save the attribute
        attrs[attr] = value

        # Remove from the required set
        required.discard(attr)

    # Did we get all required attributes?
    if required:
        raise TypeError("Missing required attributes %s" %
                        (', '.join(repr(a) for a in sorted(required))))

    # OK, instantiate and return the class
    return klass(db, **attrs)
Example #5
0
def initialize(config):
    """
    Initialize a connection to the Redis database.
    """

    # Extract relevant connection information from the configuration
    kwargs = {}
    for cfg_var, type_ in [('host', str), ('port', int), ('db', int),
                           ('password', str), ('socket_timeout', int),
                           ('unix_socket_path', str)]:
        if cfg_var in config:
            kwargs[cfg_var] = type_(config[cfg_var])

    # Make sure we have at a minimum the hostname
    if 'host' not in kwargs and 'unix_socket_path' not in kwargs:
        raise redis.ConnectionError("No host specified for redis database")

    # Look up the connection pool configuration
    cpool_class = None
    cpool = {}
    for key, value in config.items():
        if key.startswith('connection_pool.'):
            _dummy, _sep, varname = key.partition('.')
            if varname == 'connection_class':
                cpool[varname] = utils.import_class(value)
            elif varname == 'max_connections':
                cpool[varname] = int(value)
            elif varname == 'parser_class':
                cpool[varname] = utils.import_class(value)
            else:
                cpool[varname] = value
    if cpool:
        cpool_class = redis.ConnectionPool

    # Use custom connection pool class if requested...
    if 'connection_pool' in config:
        cpool_class = utils.import_class(config['connection_pool'])

    # If we're using a connection pool, we'll need to pass the keyword
    # arguments to that instead of to redis
    if cpool_class:
        cpool.update(kwargs)

        # Use a custom connection class?
        if 'connection_class' not in cpool:
            if 'unix_socket_path' in cpool:
                if 'host' in cpool:
                    del cpool['host']
                if 'port' in cpool:
                    del cpool['port']

                cpool['path'] = cpool['unix_socket_path']
                del cpool['unix_socket_path']

                cpool['connection_class'] = redis.UnixDomainSocketConnection
            else:
                cpool['connection_class'] = redis.Connection

        # Build the connection pool to use and set up to pass it into
        # the redis constructor...
        kwargs = dict(connection_pool=cpool_class(**cpool))

    # Build and return the database
    return TurnstileRedis(**kwargs)