def __new__(mcs, name, bases, namespace): """ Create a new Controller subclass. """ # Normalize any specified path prefix if 'wsgi_path_prefix' in namespace: prefix = utils.norm_path(namespace['wsgi_path_prefix'], False) # If one of our bases has wsgi_path_prefix, prepend the # first one base_pfxs = [getattr(b, 'wsgi_path_prefix') for b in bases if hasattr(b, 'wsgi_path_prefix')] if base_pfxs: prefix = base_pfxs[0] + prefix namespace['wsgi_path_prefix'] = prefix # Initialize the sets of actions and extensions actions = set() extensions = set() # Initialize serializers and deserializers serializers = {} deserializers = {} # Add the sets to the class dictionary namespace['_wsgi_actions'] = actions namespace['_wsgi_extensions'] = extensions # Add the serializers and deserializers to the class # dictionary namespace['_wsgi_serializers'] = serializers namespace['_wsgi_deserializers'] = deserializers # Find the action and extension methods for key, value in namespace.items(): # Skip internal symbols and non-callables if key[0] == '_' or key.startswith('wsgi_') or not callable(value): continue # Is it an action or extension? if hasattr(value, '_wsgi_action'): actions.add(key) elif hasattr(value, '_wsgi_extension'): extensions.add(key) # Allow inheritance in our actions, extensions, serializers, # and deserializers for base in mcs.iter_bases(bases): mcs.inherit_set(base, namespace, '_wsgi_actions') mcs.inherit_set(base, namespace, '_wsgi_extensions') mcs.inherit_dict(base, namespace, '_wsgi_serializers') mcs.inherit_dict(base, namespace, '_wsgi_deserializers') mcs.inherit_dict(base, namespace, 'wsgi_method_map') return super(ControllerMeta, mcs).__new__(mcs, name, bases, namespace)
def _route(self, action, method): """ Given an action method, generates a route for it. """ # First thing, determine the path for the method path = method._wsgi_path methods = None if path is None: map_rule = self.wsgi_method_map.get(method.__name__) if map_rule is None: # Can't connect this method LOG.warning("No path specified for action method %s() of " "resource %s" % (method.__name__, self.wsgi_name)) return # Compute the path and the method list path = utils.norm_path(map_rule[0] % self.wsgi_name) methods = map_rule[1] # Compute route name name = '%s_%s' % (self.wsgi_name, action) # Set up path path = getattr(self, 'wsgi_path_prefix', '') + path # Build up the conditions conditions = {} if hasattr(method, '_wsgi_methods'): conditions['method'] = methods if methods else method._wsgi_methods if hasattr(method, '_wsgi_condition'): conditions['function'] = method._wsgi_condition # Create the route self.wsgi_mapper.connect(name, path, controller=self, action=action, conditions=conditions, **getattr(method, '_wsgi_keywords', {}))
def test_trailing_prohibited(self): result = utils.norm_path('/foo/', False) self.assertEqual(result, '/foo')
def test_trailing_allowed(self): result = utils.norm_path('/foo/') self.assertEqual(result, '/foo/')
def test_collapse(self): result = utils.norm_path('///foo/////bar') self.assertEqual(result, '/foo/bar')
def test_leading(self): result = utils.norm_path('foobar') self.assertEqual(result, '/foobar')
def action(*methods, **kwargs): """ Decorator which marks a method as an action. The first positional argument identifies a Routes-compatible path for the action method, which must begin with a '/'. If specified, the remaining positional arguments identify permitted HTTP methods. The following keyword arguments are also recognized: * conditions Identifies a single function which will be passed the request (an instance of `webob.Request` and the match dictionary. It should return True if the route matches, and False otherwise. * code Specifies the default HTTP return code to use for the response. If not specified, defaults to 200. Note that the action method may always return a ResponseObject instance with an alternate code, if desired. All other keyword arguments will be statically passed to the action method when called. """ # Convert methods to a list, so it can be mutated if needed methods = list(methods) # Build up the function attributes attrs = dict(_wsgi_action=True) # Get the path... if methods and methods[0][0] == '/': # Normalize the path and set the attr attrs['_wsgi_path'] = utils.norm_path(methods.pop(0)) # Are we restricting the methods? if methods: attrs['_wsgi_methods'] = [meth.upper() for meth in methods] else: # Path will be computed from collection/resource and method # name attrs['_wsgi_path'] = None # Allowed methods will be based on the function name; provide # some value for the attribute so they get set by the _route() # method attrs['_wsgi_methods'] = None # If we have a condition function, set it up if 'conditions' in kwargs: condition = kwargs.pop('conditions') @functools.wraps(condition) def wrapper(req, match_dict): if isinstance(req, dict): req = webob.Request(req) return condition(req, match_dict) attrs['_wsgi_condition'] = wrapper # If we have a default code, set it up if 'code' in kwargs: attrs['_wsgi_code'] = kwargs.pop('code') # Strip out action and controller arguments kwargs.pop('action', None) kwargs.pop('controller', None) # Save additional keyword arguments if kwargs: attrs['_wsgi_keywords'] = kwargs # Now, build the decorator we're going to return def decorator(func): # Save the attributes func.__dict__.update(attrs) return func return decorator