« Yet Another Firefox Bookmarklet TrickIt doesn't take much of a Python to swallow my brain »

Funny how people only goggle over the baby

08/16/05

Permalink 05:17:39 pm, by fumanchu Email , 662 words   English (US)
Categories: CherryPy, WSGI

Funny how people only goggle over the baby

Simon Willison recently wrote a description of Django's request-handling mechanism. Here's a quick comparison with CherryPy:

When Django receives a request, the first thing it does is create an HttpRequest object (or subclass there-of) to represent that request.

CherryPy has a Request object, as well. However, it's purely an internal object; it doesn't get passed around to application code. One of the design points of CherryPy is that it allows you to write (at least a majority of) your code "like any other app"; this means that input arrives as "simple data" via function parameters, and you use the "return" statement to output data, not custom HTTP-framework objects. Point in favor of CP, IMO.

Once the object has been created, Django performs URL resolution. This is a process by which the URL specified in the request is used to select a view function to handle the creation of a response. A trivial Django application is simply one or more view functions and a configuration file that maps those functions to URLs.

Like almost every other web framework. ;) The only difference from CherryPy is that CP specifies the mapping in code, not config files. Another point to CP.

Having resolved the URL to a view, the view function is called with the request object as the first argument. Other keyword arguments may be passed as well depending on the URL configuration; see the documentation for details.

See above; CherryPy is flatter, and tends to pass data, not internal objects.

The view function is where the bulk of the work happens: it is here that database queries are made, templates loaded, HTML is generated and an HttpResponse object encapsulating the result is created. The view function returns this object, which is then passed back to the environment-specific code (mod_python or WSGI) which passes it back to the browser as an HTTP response.

Again, CherryPy is flatter, expecting you to return data, not objects. You can return a string, an iterable of strings, a file, or None, or yield any of those. Point.

This is all pretty straightforward stuff - but I skipped a couple of important details: exceptions and middleware. The view function doesn't have to return an HttpResponse; it can raise an exception instead, the most common varieties being Http404 (for file-not-found) or Http500 (for server error). In development servers these exceptions will be formatted and sent back to the browser, while in production mode they will be silently logged and a "friendly" error message displayed.

CherryPy also has user-raisable exceptions; however, they're not so low-level. Instead of Http404, you raise cherrypy.NotFound. Instead of Http3xx, you raise cherrypy.HTTPRedirect. I prefer CP's style, of course, but I don't think it's a clear "winner" over Httpxxx exceptions.

Middleware is even more interesting. Django provides three hooks in the above sequence where middleware classes can intervene, with the middleware classes to be used defined in the site's configuration file. This results in three types of middleware: request, view and response (although one middleware class can apply for more than one hook).

CherryPy has 7 such hooks; two are for errors, so let's call it 5 for a more-reasonable comparison. But see my previous post on why static hook points may not be the best approach. Still, 5 is better than 3 :). Point.

The bulk of the above code can be found in the call method of the ModPythonHandler class and the get_response method of the BaseHandler class.

That sounds like an unfortunate violation of the DRY principle. CherryPy isolates all of that nicely via the server.request() function. Are we keeping score yet?

As Django is not yet at a 1.0 release, the above is all subject to potential refactoring future change.

I can't wait to see Django 1.0! Until then, I'm going to take our adolescent web framework and go sulk in my room. ;)

15 comments

Comment from: Adrian Holovaty [Visitor] · http://www.holovaty.com/

Hey there, fumanchu,

Your point system is quite subjective. (It's very clever, though.) Clearly, Django has a different design than CherryPy. Here are some responses to your criticisms.

Disclaimer: I've never used CherryPy. I've only read the CherryPy documentation briefly.

You said: """CherryPy has a Request object, as well. However, it's purely an internal object; it doesn't get passed around to application code. One of the design points of CherryPy is that it allows you to write (at least a majority of) your code "like any other app"..."""

Yes, instead of passing a single, nicely-encapsulated request object to each view (as Django does), CherryPy appears to rely on global variables.

Sessions: http://www.cherrypy.org/trunk/docs/book/html/index.html#id3160962
Request objects: http://www.cherrypy.org/trunk/docs/book/html/index.html#id3160962

You said: """The only difference from CherryPy is that CP specifies the mapping in code, not config files. Another point to CP."""

That's tight coupling. I like the extra level of abstraction Django provides. I can point different URLs to the same view; I can swap out configuration to reuse views in different contexts; I can save myself from validating parameters in the view by handling that at the URL-mapping level. All without having to edit the actual application.

And I like the fact that, in Django, URLs are decoupled from views. CherryPy seems to hard-code URL parsing logic in the view level. Example: http://www.cherrypy.org/trunk/docs/book/html/index.html#id3160962

Please correct me if I'm wrong in my understanding here.

You said: """CherryPy is flatter, expecting you to return data, not objects. You can return a string, an iterable of strings, a file, or None, or yield any of those. Point."""

Enabling Django views to return iterables is on the to-do list. That'd be a good improvement.

You said: """CherryPy has 7 such hooks; two are for errors, so let's call it 5 for a more-reasonable comparison. ... 5 is better than 3"""

Again, this is subjective. The three Django hooks are quite sufficient.

And is this really an advantage? Hooks are easy to add. If there's a reasonable need for another hook, we'll add it.

You said: """That sounds like an unfortunate violation of the DRY principle."""

No, you missed the point on that one. Simon's comment meant that the code he was talking about lives in either of those modules -- not both of them.

Thanks for the write-up!

Adrian

08/16/05 @ 18:49
Comment from: Lewis [Visitor] · http://www.lfranklin.org

When starting on a project I am currently working on I set out to find a framework that would meet my needs. I started with Django and wareweb/paste but became frustrated rather quickly with their complexity (at least in setup. I never got either working). After a couple of other failed attempts, I tried CherryPy. Within a matter of minutes I had a website up and running and getting my software to work with CherryPy has been a breeze.

Regardless of the subjectivity of your post (which the first response has as well without acknowledging), so what. It's nice to see someone reminding us that there are other frameworks out there. The Python community has become enamored with Djando (and Ruby on Rails for that matter), forgetting that there are some excellent web frameworks that already exist, with CherryPy being one of the leaders of that pack.

08/16/05 @ 19:37
Comment from: Jacob Kaplan-Moss [Visitor] · http://jacobian.org/

It amuses me that this has to be a contest. There's the obvious "Django versus Rails" stuff, there's the "Django's template language versus TAL" stuff, and now there's "CherryPy scores more 'points' than Django". When I add features to Django, it's certainly not to score "points" or to make sure that I've got 14 more wizbags than Some Other Framework.

Fact is, we all have a lot to learn from each other. Rails' baked-in Ajax support is awesome and deserves to be emulated; CherryPy's more abstract NotFound exception is certainly a better name than Http404; I've yet to see any web framework that has an admin interface one-tenth as nice as Django's.

Unfortunately, when these inevitable comparisons turn into pissing matches it makes the lessons hard to learn. How 'bout this: I'll keep my criticism level-headed if you will yours. Sound good?

08/16/05 @ 19:45
Comment from: fumanchu [Member] Email

Jacob,

I completely agree that there's a lot we can learn from each other. Sorry if you felt the post wasn't "level-headed enough"--I always try to inject enough winks to let you know any personal spin I put on the concepts is half-hearted at best, and only present to provide entertainment in an otherwise sterile comparison. I knew that degree in theater would come in handy some day. ;)

Hope you can remain "amused". I'd love to hear any criticism you have, level-headed or not.

08/16/05 @ 23:15
Comment from: fumanchu [Member] Email

Adrian, thanks for the detailed response! :) Some reresponses:

Yes, instead of passing a single, nicely-encapsulated
request object to each view (as Django does), CherryPy
appears to rely on global variables.

Yes, it does, although the significant ones (request, response) are threadlocal objects, and therefore not truly global. Incidentally, I wrote my own framework before I started working on CP, and it passed a nicely-encapsulated request object around to each view, which then had to be unpacked within each view, and passed around to any helper functions used by those views. The "global" feel of CherryPy was a breath of fresh air, and one of the reasons I chose to move to, and contribute to, CP.

There is a potential for certain parts of CP to collide if anyone tries to serve multiple apps within the same process. That was the reason I passed a request object around in my old framework, actually. I'm at the point now where I just recommend multiple processes--it makes the code so much easier to write and maintain. We'll probably address multiple apps-per-process again before CP 2.2.

I like the extra level of abstraction Django provides.
1. I can point different URLs to the same view;
2. I can swap out configuration to reuse views in
different contexts;
3. I can save myself from validating parameters
in the view by handling that at the URL-mapping
level. All without having to edit the actual
application.

Sure; slippage can be nice. CP can do #1 in most cases and can do #2 (in code), by the way. I'm not quite sure what you mean by #3; not sure exactly what sort of validation you're talking about--correct names? all present-and-accounted-for? data types and coercion? virtual paths?

My old framework kept the URL-to-view map in configs. Boy, was that a pain, because:

1. I could point different URLs to the same view. ;) As my apps grew, this meant too many dead mapping links as I forgot to update one side of the map or the other.

2. It also enabled me to make some poor naming choices. I'd write "def invoice(req)" and "invoice.tmpl" and "invoice.css" and "invoice.js" but for some reason end up with "/inv" as a URL...harder to maintain and debug.

3. It gave application deployers license to change things only developers should care about. We had a nice long discussion about this a while back on the CP dev list; my personal opinion is that nothing should be in config files unless you expect deployers to modify it. That code/config boundary is as natural a split as you're going to get between developers and deployers, so milk it.

CherryPy seems to hard-code URL parsing logic in the view level.

If I understand that sentence correctly, the answer is "not usually". In CP, view methods are attached to an object tree, and that tree is walked (by the core) to dispatch a URL to the appropriate view method. Sometimes, an app developer will dispatch further within a "default" method, but that's a separate step.

A truly enterprising soul could override the core's dispatcher entirely in CP with a "beforeMain" filter.

My old framework allowed for various "resolvers", by the way; I came to the conclusion that IWGNI (I warn't gonna need it), and I haven't, nor have I seen anyone ask for anything like that for CP. If CP were to allow for various resolvers, it would probably be along the lines of my previous post (http://www.amorhq.net/blogs/index.php/fumanchu/2005/07/30/plugin_madness); that is, dynamic resolvers would not be implemented parametrically (and certainly not via configs), but procedurally. Oh, but it looks like Django hard-codes the RegexURLResolver anyway ("resolver = urlresolvers.RegexURLResolver(r'^/', ROOT_URLCONF)"). Anyway, enough on that tangent.

The three Django hooks are quite sufficient...And is this really an advantage?

No, as I hope I hinted (where's my "elbow in the ribs" emoticon gotten off to?). In fact, I would say writing a (worthwhile) filter is probably the most difficult thing one could do within CP, partly because it can be tough to decide which hook to use unless you're already pretty familiar with the request-processing internals.

Now I'm curious about something else: does Django support incremental output? I can't see it browsing through the code. I know that is a constant additional burden on middleware design for CP.

08/17/05 @ 00:26
Comment from: hugo [Visitor] · http://hugo.muensterland.org/

Two points that somehow adrian missed to address:

- django not only passes the Request object, but parts of the URL as normal data parameters, too - so it's a mixed bag with regard to parameters you need to use, as the request object is often just passed along to the response object but not needed for getting at paramters.

- django configfiles are python modules. So the mapping is done fully python, no dead config data. So django does specify the mapping in code, too. Actually the no-dead-configs was one major point Django scored with me. The other was the automatically admin interface, that will solve 70% of my day work - and since I am a lazy bastard that one wins big :-)

08/17/05 @ 01:27
Comment from: Sylvain Hellegouarch [Visitor] · http://www.defuze.org/oss/

IMO, the bottom line really depends on your personal approach to development. CP is a dream to me as it doesn't force me in anyway to follow a specific design pattern. It only gives me a very simple access to HTTP.

For instance, most of the time I handle XML resources and I like to use XSLT to transform them into XHTML or whatever, now I had only to write a filter for CP based on 4Suite and that's all... I don't have to transform my XML documents to work with a dedicated template system like Django has.

Now If I were a guy who likes very structured and high level bricks then I'll certainly be using Django or one of the many other frameworks available (http://wiki.python.org/moin/WebProgramming). But it's not the case.

Freedom is the key for my personal perspective :)

08/17/05 @ 04:07
Comment from: Sylvain Hellegouarch [Visitor] · http://www.defuze.org/oss/

eh eh besides that, it looks like CherryPy and Django have things in common already :)

http://code.djangoproject.com/ticket/113

08/17/05 @ 04:41
Comment from: Luke [Visitor]

Personally I like the HttpXXX exception names better -- I know the HTTP status codes and using those as names saves me the trouble of learning someone else's names for them!

08/17/05 @ 11:25
Comment from: fumanchu [Member] Email

Luke,

That's fine and I understand "knowing the codes". However, it's not as if CP has a separate exception for each code, each with its own name. HTTP-status exceptions are only used for easing control-flow in two cases, so you only need to remember two names at most:

404: The NotFound exception is used primarily and automatically by the core, so CP app developers never need to "know" it. However, it's available if they want to roll their own URL dispatching.

3xx: All of the 300 codes are usable via the single HTTPRedirect exception by passing the status as an init param; for example, "raise cherrypy.HTTPRedirect(url, 301)". If you don't specify a status, it selects 302 or 303 for you, as appropriate.

If you want some other status, you would instead set it directly, instead of raising; for example, "cherrypy.response.status = 500". You could also use a string if you wanted to change the default reason-code: "cherrypy.response.status = '500 Ouch, that hurt'". Note that this technique also allows you to return a response body with an error code if you like (and therefore conform to the spec more closely, in some cases, than most frameworks allow by default).

08/17/05 @ 13:56
Comment from: Adrian Holovaty [Visitor] · http://www.holovaty.com/

In response to fumanchu's latest comment:

In Django, you'd just return an HttpResponseNotFound instance instead of an HttpResponse instance. You can also return HttpResponseServerError, etc. These let you set the status code while still having full control over the page's content.

08/17/05 @ 14:59
Comment from: Uche [Visitor] · http://copia.ogbuji.net

Adrian, CherryPy of course also gives you full control of status as well as response page's content.

Luke, I prefer CP's approach of using idioms that the typical Web user would understand, but still giving you the option of expressing a precise HTTP code if you need to.

08/18/05 @ 07:24
Comment from: Luke Stark (A different Luke) [Visitor] · http://starkaudio.com

I've seen a lot of entries from people who have used one or the other, but none that have built the same project twice. Has anyone actually duplicated a project in CP and Django and seen how the flavors differ?

08/19/05 @ 04:28
Comment from: Kevin Smith [Visitor] · http://cherryslush.python-hosting.com/

CherryPy is so direct and easy to use there seems no need to have the overhead of a framework. I don't think the data I work with is more complicated, or the user interfaces more unusual, but after spending years fighting frameworks I find the lack of a framework refreshing.

Personally I've with toyed creating an app in both, but for my purposes, the admin interface (although very pretty and robust) did not do what I needed it to. Not that it can't, but it's not worth my effort to mold it.

I think this blog post exists because anyone who uses CP, probably feels CherryPy deserves waaay more limelight and buzz than it's been getting. CP truly makes web programming fun again.

After seeing the Hello World tutorial, I was hooked.
http://www.cherrypy.org/file/trunk/cherrypy/tutorial/tut01_helloworld.py

Too bad the Django framework doesn't use CherryPy :)

08/19/05 @ 14:41
Comment from: fumanchu [Member] Email

Too bad the Django framework doesn't use CherryPy :)

It could with very little effort. ;)

08/19/05 @ 15:28

Leave a comment


Your email address will not be revealed on this site.

Your URL will be displayed.

Please enter the phrase "I am a real human." in the textbox above.
(Line breaks become <br />)
(Name, email & website)
(Allow users to contact you through a message form (your email will not be revealed.)
June 2018
Sun Mon Tue Wed Thu Fri Sat
 << <   > >>
          1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30

Search

The requested Blog doesn't exist any more!

XML Feeds

powered by b2evolution