durusmail: quixote-users: Accept-aware functions via metaclasses
Accept-aware functions via metaclasses
2003-09-25
2003-09-26
Re: Accept-aware functions via metaclasses
2003-09-26
2003-09-26
[PATCH] http response code enumeration
2003-09-26
2003-09-26
2003-09-26
Accept-aware functions via metaclasses
John Belmonte
2003-09-26
I wrote:
> In my design, I decided that making handlers according to HTTP method
> was most important.  Instead of using a meta class (which I know little
> of) to implement this, I simply made a base class that scanned its
> function attributes in __init__.  A nice side effect was being able to
> automatically generate a Method Not Allowed response with appropriate
> Allowed header (assuming the list of accepted methods is static).

Someone asked me for the code, so here it is.  I think it's
self-contained, except for the HttpError exception, which should be obvious.

Note: while Quixote provides a table of HTTP response messages, it does
not enumerate the response codes themselves (for example,
METHOD_NOT_ALLOWED).  So application developers are left to do this on
their own :-(.

-John


--
http:// if   le.o  /
class HttpResource:
    """Base class for HTTP resources

    To handle a certain HTTP method "FOO", derived classes should have a class
    method called "handle_method_FOO" that takes a HTTPRequest object.

    If there is no handler for the requested method, then a Method Not Allowed
    response will be generated.  The response will include the required "Allow"
    header.  By default, this header will be set according to the defined
    method handler functions.  In the case that the allowed methods change
    dynamically, list "allowed_methods" is a class instance variable that may
    be modified as needed.
    """

    HANDLER_PREFIX = 'handle_method_'

    def __init__(self, resource_name):
        self.resource_name = resource_name

        def is_handler(attr):
            return attr.startswith(self.HANDLER_PREFIX)

        def handler_to_method(attr):
            return attr[len(self.HANDLER_PREFIX):]

        handler_names = filter(is_handler, dir(self))
        self.allowed_methods = map(handler_to_method, handler_names)

    def __call__(self, request):
        """HTTP request handler"""

        method = request.get_environ('REQUEST_METHOD')
        handler = getattr(self, self.HANDLER_PREFIX + method, None)
        if handler:
            return handler(request)
        else:
            # Method Not Allowed response MUST contain Allow header (RFC-2616).
            # Obviously Allow should be non-null and not include the method
            # we are currently rejecting.
            assert(len(self.allowed_methods) > 0)
            assert(method not in self.allowed_methods)
            headers = { 'Allow': ', '.join(self.allowed_methods) }
            raise HttpError(http_status.METHOD_NOT_ALLOWED,
                'Method "%s" not allowed by this resource.' % method,
                extra_headers = headers)
reply