pylons gotchas

pylons gotchas

Benno was over, hacking on the LCA 2007 website with me yesterday, and we hit two gotchas, both I knew about but when I explained them to him they sounded silly.

c considered harmful.

c is a request-local global object that you can attach objects to, which is useful as a way of passing data from the controller to the template code – when you’re calling a parameterised template you might not know at call time what the args the template wants are, but you can pass them all in on c. If you’re using some pattern like a mixin CRUD class for generalising common data operations, then the code that actually calls the template doesn’t know what the object is, but the template it’s calling does.

c has the magical property that it has overloaded __getattr__ to return an empty string if the attribute is not found. This is a mixed blessing; your templates can access an attribute that hasn’t been attached and it’ll mostly cope with it. (Problems happen when you try to access attributes of nonexistent attributes, and you get the confusing message ‘str has no attribute X’.)

However, this means you hide bugs; you’ve forgotten to attach the object you want to c and then your code runs fine; it’s the users who find the problem after deployment, not during development. Having a __getattr__ that throws exceptions means you find out about these problems a lot sooner.

I think both of these points show that c in general is a bad idea; you should make use of explicit args so that your template interface is clearly defined – I haven’t yet found a nice way of doing it that is as easy as or better than using c though.

Myghty expressions that evaluate to False return empty strings.

We had a simple construct like so:

1
    Count: <% len(c.review_collection) %>

which has the interesting property of evaluating to '’ when c.review_collection is empty; len() returns 0 which is False.

This is pretty retarded; I suspect there’s a shortcut along the lines of:

1
2
3
    content = evaluate_fragment("len(c.review_collection)")
    if content:
        write(content)

when these inline blocks are rendered; the if block clearly will fail to trigger when the inline block evaluates to 0, False, [], or {}. I can’t think of a case where this is a good thing.

The workaround is to wrap the len() call in str(), so that the fragment doesn’t evaluate to false.

1
    <% str(len(c.review_collection)) %>

More gotchas as they come to hand.