durusmail: quixote-users: Re: 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
Re: Accept-aware functions via metaclasses
Graham Fawcett
2003-09-26
John Belmonte wrote:
> Graham Fawcett wrote:
>
>> I've been writing a Quixote app in a somewhat RESTful style, that
>> needs to do content negotiation on certain requests. By 'content
>> negotation', I mean that I need to examine the Accept header in the
>> request, and return an appropriate response in one of the MIME types
>> declared there.
>
>
> I'm also designing an application in REST style using Quixote.  I don't
> have any criticisms of your solution, but I'd like to make a few
> observations.
>
>> A recurrent pattern was cropping up in my code:
>>
>> def _q_index(req):
>>     acceptlist = req.environ['HTTP_ACCEPT'].split(',')
>>     if 'text/html' in acceptlist:
>>         # return a text/html representation
>>     elif 'text/xml' in acceptlist:
>>         # return a text/xml representation
>>     ...
>
>
> There is no need for this.  See
> quixote.http_request.HTTPRequest.get_accepted_types.

Thanks, John -- I had missed this method!


>
>>         def text_html [html] (cls, req):
>>             some_header(req)
>>             '

An HTML response!

' >> some_footer(req) >> def text_xml [html] (cls, req): >> '' >> def text_ [plain] (cls, req): >> 'A response to text/* requests' > > > I'd guess that in many designs, XML is the "source" format, with HTML > and plain text being generated by transforming the XML. So to me, this > can all be automated even more, and doesn't warrant splitting into > separate handlers. Yes, that came to mind for me too. My reasoning is similar, though a bit vague. Here's what's in my head, feel free to comment. What I like: I like the idea of separating out 'dispatch based on Accept', apart from actual presentation code. (Dispatching based on HTTP method or whatever criteria would make sense as well.) And classes make sense for this kind of thing, of course, since they can mix in, inherit or otherwise acquire the dispatching logic without too much clutter in the presentation code. Where I'm headed: generally, the output of my methods will be a representation of some basic data structure (a dict, a list of tuples, etc.) and there are no DTDs to which I must ahdere in this app. For HTML, a 'pretty-print' of the structure will be sufficient in most cases; I could XML and CSS or transforms, I suppose. But I also want to be able to output the structure in one or more of XML, YAML, and eval'uable Python text. 'Format-agnostic' might be an accurate decription of my intent. What I want, eventually: In response to a request, a 'Quixote method' builds a message as a Python data structure. The method's dispatcher takes the message and passes it to an appropriate handler for rendering into the desired output format. Conversions to python-text, XML, YAML can be generalized and handled at a superclass level. In the corner cases where I need to customize the output of a given Quixote method in a given Content-Type (e.g., a particular 'index_html' needs a special 'text/html' rendering), I can just provide a 'text_html()' method in the class in question. None of this refutes your 'XML as source', of course. ;-) I'm just trying to use Python data as source instead. > >> Back to acceptfunction. A similar approach could be used to make >> 'function classes' for functions that need to dispatch based on method >> (GET, PUT, ...), user agent, locale, etc. (Though I dread to think >> about having to write a _q_index.text_html.put.en_us() function and >> its hundred-odd companions!) > > > 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__. Metaclasses may have been overkill, I probably could do everything I needed with inheritance. It seemed the easiest way at the time... > 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). Yes, I wanted to do something similar with my 406 Not Acceptable responses (they're supposed to return a list of supported content-types). In its current form, the naming conventions in my code prevent this: for example, my handler method 'application_octet_stream()' cannot be mapped onto the MIME name 'application/octet-stream' without additional information. I will probably just change my naming convention (e.g. 'application_octet_dash_stream()' is ugly but it maps). Thanks very much for the feedback! -- Graham
reply