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
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
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)
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)
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)