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.

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