Thursday, July 25, 2013

A Nose Specific Django Settings File

The Python Nose test framework has a several advantages over Django's default test framework.  I personally wanted to use Nose because of the xunit plugin which can output results in xunit format.  I needed this output format so that test results could be interpreted by Bamboo, the continuous integration solution we're using at my work.

For using Nose with Django, the django-nose package is an obvious choice.  However, it requires setting several settings values in your settings.py file that were only used by Nose.  Also, I like being able to run my tests using Django's default test runner.  I'm familiar with the output format, with the syntax for specifying how to run a single test, etc. and I didn't want to limit myself to running only Nose tests.

The approach I took was to create a new settings file for running nose tests.  This would contain just the settings needed for configuring django-nose, and import the main settings file to fill in the rest.  This is kind of the opposite of the usual pattern of importing a "local_settings.py" file at the bottom of your main "settings.py" to specify scenario specific settings (e.g. development vs production).

The comments in the script specify how to use it.  I hope it's helpful.

Wednesday, July 10, 2013

Duck Typing in Python

It has happened before, and it was happening again.  I had a weird problem, coded up an "interesting" solution, and then tried to find out why I felt strange about it.

In this case, I was writing a test function in a Django project to loop over a set of Tastypie api endpoints and make sure each endpoint responded without any errors.  To allow this test to grow with the file describing the api endpoints and to follow basic DRY principles I decided to import that file, find all the classes of a given type find the attribute I needed to build up the url of the corresponding api endpoint, and use the test client to render that url and check for errors.

My first attempt looked like this:


It may work, but it's not pretty. I'm explicitly checking whether each object I'm looking for is of the correct class, then I'm checking for an attribute, and after that I'm still having to wrap everything in an Exception to handle the case where the first argument to "issubclass" is not, in fact, a class.

So, with the smell of bad code wafting through the air, I went off in search of better solutions.  In this search I started in the place where all good programmer start: StackOverflow.

I started with this question about checking if a class is a subclass of another class.  At first I dismissed the comment about "python is not java" as a preachy comments on dynamic languages, but that got me thinking: I've heard the same people advocating duck typing and at the same time saying not to use exceptions to control flow.  I hadn't really put these 2 together solidly before, but now that I have this problem to work on, I see that these are quite related, and you can't have it both ways.  Either you type check, or you handle the unexpected with exceptions.

Coming from a HPC world, I instinctively avoided exceptions because they are usually though of as slow.  But after reading this very well phrased question on the topic of asking permission vs asking forgiveness (i.e. type checking vs. exception handling), my mind was starting to change.  In python the performance penalty is not so large that Exceptions need only be used for "exeptional" circumstances. After all, iterators are controlled with the StopIteration Exception, and that's part of the core language.

My mind was made after reading a third great question and response explicitly addressing duck typing.  The top answer recommends using Exceptions whenever possible as long as nothing really strange is going to happen if you try an operation on the wrong type of object.  Most of the time it's better to just document your functions well, let your users decide if they want to use it, and raise and Exception up to them if something went wrong.

So after all this, I went back and tried to put back together my simple function using what I learned.  I think the second take is more readable and probably just as fast as the original (the running of the test client for each url is definitely the bottleneck here anyway).  The substitution of the getmembers function of the Inspect module helps too.

If you were running into a similar problem with smelly code or preachy internet pythonistas, I hope this helps clear things up.

P.S. - The code snippets were embedded by adding the following snippet directly in the body of the post using the HTML editor feature of Blogger:

<script src="https://gist.github.com/[username]/[gistID]/[gistVersion].js"></script>