An Experiment In Scotch

I write to discover what I believe

Tag: pylons

Route Gotchas In Pylons

I’ve been working on a Pylons app quite a bit lately and occasionally I run across issues that warrant documentation on the interwebs. That happened today concerning routes and how Pylons deals with them.

If you start getting an Import error that says “No module named content found”, you’ve run into it. According to the Pylons book, “Routes has a surprising legacy feature that means that if you don’t specify a controller and an action for a particular route, the implicit defaults of controller=’content’ and action=’index’ will be used for you”. This is certainly surprising to me and as always, things that are implicit tend to annoy me. Luckily, you can change this behavior. In your config/routing.py file, set map.explicit = True and then you’ll need to alter the route that is giving you problems to make the controller and action explicit. For example:

Before
[sourcecode language=”python”]def make_map(config):
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config[‘pylons.paths’][‘controllers’],
always_scan=config[‘debug’])
map.minimization = False
map.explicit = False

# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect(‘/error/{action}’, controller=’error’)
map.connect(‘/error/{action}/{id}’, controller=’error’)

# CUSTOM ROUTES HERE
map.connect(‘schedule_entry’, ‘schedule/index/{scheduleDay}’)
map.connect(‘/{controller}/{action}’)
map.connect(‘/{controller}/{action}/{id}’)

return map
[/sourcecode]

As you can see, the default behavior is for map.explicit to be set to False. The first route under CUSTOM ROUTES HERE is a named route that to my eye should match schedule up with a controller and index up with the action. Unfortunately, instead it tries to find the default implicit controller “content” which it can’t find and that throws the ImportError listed above. To fix it do this:

After
[sourcecode language=”python”]
def make_map(config):
"""Create, configure and return the routes Mapper"""
map = Mapper(directory=config[‘pylons.paths’][‘controllers’],
always_scan=config[‘debug’])
map.minimization = False
map.explicit = True

# The ErrorController route (handles 404/500 error pages); it should
# likely stay at the top, ensuring it can always be resolved
map.connect(‘/error/{action}’, controller=’error’)
map.connect(‘/error/{action}/{id}’, controller=’error’)

# CUSTOM ROUTES HERE
map.connect(‘/schedule/index/{scheduleDay}’, controller=’schedule’, action=’index’)
map.connect(‘/{controller}/{action}’)
map.connect(‘/{controller}/{action}/{id}’)

return map
[/sourcecode]

Now the map is set to explicit and the controller and action are explicitly specified which works just fine. This all may be an artifact my novice understanding of Routes but since the book documents it this way, I guess this is the way I’m going to do it.

TDD with Pylons

I’ve written about test driven development and Pylons before but there have apparently been some changes to how it all works since Pylons 1.0. I didn’t run across anything in the documentation detailing the changes, specifically to the template context global and how you access it in your tests.

From the Pylons docs on testing:

Pylons will provide several additional attributes for the paste.fixture response object that let you access various objects that were created during the web request:

  1. session — Session object
  2. req — Request object
  3. c — Object containing variables passed to templates
  4. g — Globals object

To use them, merely access the attributes of the response after you’ve used a get/post command:
[sourcecode language=”python”]response = app.get(‘/some/url’)
assert response.session[‘var’] == 4
assert ‘REQUEST_METHOD’ in response.req.environ[/sourcecode]

As it turns out, that’s sort of true. I have a test that looks like this:

[sourcecode language=”python”]from nbapowerrank.tests import *

class TestGamedetailsController(TestController):

def test_index(self):
response = self.app.get(url(controller=’admin/gamedetails’, action=’index’))
self.assertEqual(len(response.c.games) > 0, True)[/sourcecode]

and a controller that looks like this:

[sourcecode language=”python”]from nbapowerrank.lib.base import BaseController, render
from nbapowerrank.model import meta
from nbapowerrank.model.game import Game

log = logging.getLogger(__name__)

class GamedetailsController(BaseController):
def __before__(self):
self.game_q = meta.Session.query(Game)

def index(self):
yesterday = datetime.today() – timedelta(1)
c.games = self.game_q.filter_by(gamedate=yesterday).all()

return render(‘/gamedetails.mako’)[/sourcecode]

Unfortunately, contra the documentation that says the “c” alias will be available on the test response object, that test always fails with an AttributeError stating that in fact, the c attribute does not exist. It frustrated me even more because all the other attributes that are supposed to be on the response like session and g were in fact there. After doing some random digging, I came across this in the Pylons 1.0 roadmap: “Deprecate pylons.c, pylons.g, and pylons.buffet. These have been disrecommended since 0.9.7.”

Apparently, they are/have deprecated using the c alias for tmpl_context even though when you create a controller under Pylons 1.0, it still aliases the context as c. Sigh. So in order to test data that you have added to your context for templating, your test should use the explicit tmpl_context instead of the c like this:

[sourcecode language=”python”]from nbapowerrank.tests import *

class TestGamedetailsController(TestController):

def test_index(self):
response = self.app.get(url(controller=’admin/gamedetails’, action=’index’))
self.assertEqual(len(response.tmpl_context.games) > 0, True)[/sourcecode]

And then all will be well with your world. I do have to say that compared to both ASP.Net MVC and Rails, the testing support in Python/pylons seems to be a second class citizen. I’m not sure why that is because as a dynamic language, it seems to benefit greatly from a TDD approach. Maybe it’s just me getting back into a framework after a year’s worth of changes.

Anyway, that may help some people out there trying to search in the googleplex about TDD and Pylons.

TDD With Python and Pylons

I’ve been doing some development on a website using Python and the Pylons web framework. I’m trying to stay pretty strict with Test Driven Development (TDD) though I run into problems because I’m still a complete novice with Pylons and a half-complete novice with Python. In my experience so far, unit tests are moderately difficult in Pylons and turn out to be something closer to the bastard stepchild of a redneck unit test and a 5th Avenue socialite mom. I feel that way because the unit tests require the Pylons framework to be set up correctly. They also often go outside their boundaries and I haven’t looked into any mock frameworks, though I have the feeling that using a mock framework with a dynamic language like Python is probably a stupid thing to say in public.

Regardless, I have really started to enjoy working with Pylons and that stems from the actual functional tests that are available through the framework. Specifically, my development flow has been something like this:

  • Write new functional test of the web site
  • Run tests to see them error out. Typical error message is that an action isn’t implemented on the controller.
  • Implement the basic controller and the action but leave out the functionality under test.
  • Rerun the test to see it fail.
  • Implement the functionality necessary to get a passing test. This often includes implementing database tables, keys, getting SqlAlchemy set up to correctly map data to objects and creating new templates for HTML.

The functional tests are nice because while they aren’t confirming look and feel type stuff, they at least put the flow of the application under test. I’m a purist when it comes to having unit tests only test the code they are intended for but I’m not a purist when it comes to writing unit tests first as the only way to design the application. With a website like this, I’m pretty happy writing functional tests to drive out the design of the web site.

Here are some code snippets from my current website (try to ignore the fact that this looks like it might have something to do with horses and the Kentucky Derby, especially if you work for the NSA.)

First a test:

from darlydowns.tests import *
from darlydowns.model import meta
from darlydowns.model import horse


class TestHorseController(TestController):
    def test_index(self):
        # setting up a temp horse to make sure one exists for the test
        tempHorse = horse.Horse('my temp', 'my description')
        meta.Session.save(tempHorse)
        meta.Session.commit()

        response = self.app.get(url_for(controller='horse'))
        ## Test response...
        assert len(response.c.horses) > 0

        meta.Session.delete(tempHorse)
        meta.Session.commit()

Here we have a test that tests the response from a request for a URL that is handled by the controller “horse”. This controller grabs all the horses in the database and displays them in a table. The test saves a temp horse, gets the list, verifies the list contains at least 1 horse and then deletes the temp horse to clean up after itself.

Here’s the controller code that allows the test to pass:


import logging

from darlydowns.model import meta
from darlydowns.lib.base import *
from darlydowns.model import horse
from darlydowns.model.horse import Horse

log = logging.getLogger(__name__)

class HorseController(BaseController):

    def index(self):
        c.horses = [horse for horse in meta.Session.query(Horse).all()]
        return render('/horses.mako')

One of the beauties of Pylons is how little code is required to do something, once the project is set up. Here our HorseController has an action of “index” which is defined as a method. It grabs all the horses from the database and then uses a list comprehension to collect them into the c.horses variable. It then renders the template “horses.mako” which knows how to layout the web page using the horses found in the database.

Once this was done, I wrote code to save a horse, all driven out by the functional tests. I’ve been pretty happy with how the design is driven from these tests as it’s often quite clear where to proceed next in the application from the last test. Lots of times, other functionality comes up that doesn’t logically flow next but I just add that to a growing todo list in the project to make sure nothing is skipped.

Pylons takes a little getting used to, especially since I come from a static, everything in once solution sort of background. With Pylons, you need to learn Routes and Mako and SqlAlchemy but once you get your head around all those tools, it’s a joy to work with.

UPDATE: Welcome to everyone coming here from the Python subreddit. If you have any tips for testing using Pylons, feel free to drop me a comment. I’d love to hear about other people’s experiences.