<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-7176062489626496619</id><updated>2011-10-09T13:44:49.733-04:00</updated><category term='pc'/><category term='disclaimer'/><category term='education'/><category term='gsoc'/><category term='admin'/><category term='pip'/><category term='al'/><category term='pygtk'/><category term='multiprocess'/><category term='sql alchemy'/><category term='template'/><category term='pycon'/><category term='response'/><category term='python'/><category term='easy_install'/><category term='ply'/><category term='tips'/><category term='rails'/><category term='turbogears'/><category term='forms'/><category term='review'/><category term='virtualenv'/><category term='c++'/><category term='laptop'/><category term='yacc'/><category term='unladen-swallow'/><category term='talk'/><category term='parse'/><category term='foreignkey'/><category term='models'/><category term='deployment'/><category term='college'/><category term='gtk'/><category term='ajax_validation'/><category term='web2py'/><category term='sql object'/><category term='django'/><category term='book'/><category term='go'/><category term='compile'/><category term='gae'/><category term='filter'/><category term='webfaction'/><category term='PHP'/><category term='obama'/><category term='jquery'/><category term='eee pc'/><category term='tests'/><category term='economics'/><category term='internals'/><category term='programming-languages'/><category term='software'/><category term='netbook'/><category term='virtualenvwrapper'/><category term='metaclass'/><category term='orm'/><category term='lex'/><category term='release'/><category term='ubuntu'/><category term='blogging'/><category term='vcs'/><category term='pypy'/><category term='compiler'/><title type='text'>Lazy Pythonista</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>83</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1352615972889662187</id><published>2009-12-02T23:24:00.002-05:00</published><updated>2009-12-02T23:27:43.650-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='education'/><category scheme='http://www.blogger.com/atom/ns#' term='college'/><title type='text'>A few thoughts on education</title><content type='html'>Lately I've been thinking quite a lot about education, both my own and in general.  I ended up writing quite a bit about it.  You can find all my thoughts &lt;a href="http://dl.dropbox.com/u/1015145/education.pdf"&gt;here&lt;/a&gt; (PDF warning).  I stuck it in a PDF because I think it's a more canonical form.  I'm interested in hearing any thoughts people have, both about what I wrote and about education.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1352615972889662187?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1352615972889662187/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/12/few-thoughts-on-education.html#comment-form' title='23 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1352615972889662187'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1352615972889662187'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/12/few-thoughts-on-education.html' title='A few thoughts on education'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>23</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3095805343906376697</id><published>2009-12-01T00:56:00.000-05:00</published><updated>2009-12-01T00:57:20.716-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>A month in review</title><content type='html'>I've now been blogging for 30 days in a row, so tonight is just going to be a simple post to finish the month of.  First of all, WOW, another month of blogging every day completed.  This month was great fun, and I managaged to keep the bullshit filler posts to a minimum (3 or 4 by my count).  I also finished the month with 42700 total hits (easily a record), including hitting reddit and Hacker News several times.  It's been great fun writing every night, hopefully I'll be able to keep up the regular posts, but for now I plan on taking a nice long nap, then I'll get back to the requests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3095805343906376697?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3095805343906376697/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/12/month-in-review.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3095805343906376697'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3095805343906376697'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/12/month-in-review.html' title='A month in review'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2049689557439226265</id><published>2009-11-30T00:42:00.000-05:00</published><updated>2009-11-30T00:43:19.668-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='metaclass'/><title type='text'>You Built a Metaclass for *what*?</title><content type='html'>Recently I had a bit of an interesting problem, I needed to define a way to represent a C++ API in Python.  So, I figured the best way to represent that was one class in Python for each class in C++, with a functions dictionary to track each of the methods on each class.  Seems simple enough right, do something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class String(object):&lt;br /&gt;        functions = {&lt;br /&gt;            "size": Function(Integer, []),&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We've got a String class with a functions dictionary that maps method names to Function objects.  The Function constructor takes a return type and a list of arguments.  Unfortunately we run into a problem when we want to do something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class String(object):&lt;br /&gt;        functions = {&lt;br /&gt;            "size": Function(Integer, []),&lt;br /&gt;            "append": Function(None, [String])&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If we try to run this code we're going to get a NameError, String isn't defined yet.  Django models have a similar issue, with recursive foreign keys.  Django's solution is to use the placeholder string "self", and have a metaclass translate it into the right class.  Also having a slightly more declarative API might be nice, so something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class String(DeclarativeObject):&lt;br /&gt;        size = Function(Integer, [])&lt;br /&gt;        append = Function(None, ["self"])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So now that we have a nice pretty API we need our metaclass to make it happen:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    RECURSIVE_TYPE_CONSTANT = "self"&lt;br /&gt;&lt;br /&gt;    class DeclarativeObjectMetaclass(type):&lt;br /&gt;        def __new__(cls, name, bases, attrs):&lt;br /&gt;            functions = dict([(n, attr) for n, attr in attrs.iteritems()&lt;br /&gt;                if isinstance(attr, Function)])&lt;br /&gt;            for attr in functions:&lt;br /&gt;                attrs.pop(attr)&lt;br /&gt;            new_cls = super(DeclarativeObjectMetaclass, cls).__new__(cls, name, bases, attrs)&lt;br /&gt;            new_cls.functions = {}&lt;br /&gt;            for name, function in functions.iteritems():&lt;br /&gt;                if function.return_type == RECURSIVE_TYPE_CONSTANT:&lt;br /&gt;                    function.return_type = new_cls&lt;br /&gt;                for i, argument in enumerate(function.arguments):&lt;br /&gt;                    if argument == RECURSIVE_TYPE_CONSTANT:&lt;br /&gt;                        function.arguments[i] = new_cls&lt;br /&gt;                new_cls.functions[name] = function&lt;br /&gt;            return new_cls&lt;br /&gt;    &lt;br /&gt;    class DeclarativeObject(object):&lt;br /&gt;        __metaclass__ = DeclarativeObjectMetaclass&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And that's all their is to it.  We take each of the functions on the class out of the attributes, create a normal class instance without the functions, and then we do the replacements on the function objects and stick them in a functions dictionary.&lt;br /&gt;&lt;br /&gt;Simple patterns like this can be used to build beautiful APIs, as is seen in Django with the models and forms API.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2049689557439226265?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2049689557439226265/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/you-built-metaclass-for-what.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2049689557439226265'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2049689557439226265'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/you-built-metaclass-for-what.html' title='You Built a Metaclass for *what*?'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8857791624422569644</id><published>2009-11-29T01:07:00.001-05:00</published><updated>2009-11-29T01:07:45.894-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='tests'/><title type='text'>Getting Started with Testing in Django</title><content type='html'>Following yesterday's post another hotly requested topic was testing in Django.  Today I wanted to give a simple overview on how to get started writing tests for your Django applications.  Since Django 1.1, Django has automatically provided a tests.py file when you create a new application, that's where we'll start.&lt;br /&gt;&lt;br /&gt;For me the first thing I want to test with my applications is, "Do the views work?".  This makes sense, the views are what the user sees, they need to at least be in a working state (200 OK response) before anything else can happen (business logic).  So the most basic thing you can do to start testing is something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    from django.tests import TestCase&lt;br /&gt;    class MyTests(TestCase):&lt;br /&gt;        def test_views(self):&lt;br /&gt;            response = self.client.get("/my/url/")&lt;br /&gt;            self.assertEqual(response.status_code, 200)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;By just making sure you run this code before you commit something you've already eliminated a bunch of errors, syntax errors in your URLs or views, typos, forgotten imports, etc.  The next thing I like to test is making sure that all the branches of my code are covered, the most common place my views have branches is in views that handle forms, one branch for GET and one for POST.  So I'll write a test like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    from django.tests import TestCase&lt;br /&gt;    class MyTests(TestCase):&lt;br /&gt;        def test_forms(self):&lt;br /&gt;            response = self.client.get("/my/form/")&lt;br /&gt;            self.assertEqual(response.status_code, 200)&lt;br /&gt;            &lt;br /&gt;            response = self.client.post("/my/form/", {"data": "value"})&lt;br /&gt;            self.assertEqual(response.status_code, 302) # Redirect on form success&lt;br /&gt;            &lt;br /&gt;            response = self.client.post("/my/form/", {})&lt;br /&gt;            self.assertEqual(response.status_code, 200) # we get our page back with an error&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now I've tested both the GET and POST conditions on this view, as well the form is valid and form is invalid cases.  With this strategy you can have a good base set of tests for any application with not a lot of work.  The next step is setting up tests for your business logic.  These are a little more complicated, you need to make sure models are created and edited in the right cases, emails are sent in the right places, etc.  &lt;a href="http://docs.djangoproject.com/en/dev/topics/testing/"&gt;Django's testing documentation&lt;/a&gt; is a great place to read more on writing tests for your applications.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8857791624422569644?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8857791624422569644/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/getting-started-with-testing-in-django.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8857791624422569644'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8857791624422569644'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/getting-started-with-testing-in-django.html' title='Getting Started with Testing in Django'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-766919506865200259</id><published>2009-11-28T00:14:00.002-05:00</published><updated>2009-11-28T19:39:20.521-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Django and Python 3</title><content type='html'>Today I'm starting off doing some of the posts people want to see, and the number one item on that list is Django and Python 3.  Python 3 has been out for about a year at this point, and so far Django hasn't really started to move towards it (at least at a first glance).  However, Django has already begun the long process towards moving to Python 3, this post is going to recap exactly what Django's migration strategy is (most of this post is a recap of a message &lt;a href="http://www.b-list.org/"&gt;James Bennett&lt;/a&gt; sent to the django-developers mailing list after the 1.0 release, available &lt;a href="http://groups.google.com/group/django-developers/msg/0888b1c8f2518059"&gt;here&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;One of the most important things to recognize in this that though there are many developers using Django for smaller projects, or new projects that want to start these on Python 3, there are also a great many more with legacy (as if we can call recent deployments on Python2.6 and Django 1.1 legacy) deployments that they want to maintain and update.  Further, Django's latest release, 1.1, has support for Python releases as old as 2.3, and a migration to Python 3 from 2.3 is nontrivial.  However, it is significantly easier to make this migration from Python 2.6.  This is the crux of James's plan, people want to move to Python 3.0 and moving towards Python 2.6 makes this easier for them and us.  Therefore, since the 1.1 release Django has been removing support for one point version of Python per Django release.  So, Django 1.1 will be the last release to support Python 2.3, 1.2 will be the last to support 2.4, etc.  This plan isn't guaranteed, if there's a compelling reason to maintain support for a version for longer it will likely override this plan (for example if a particularly common deployment platform only offered Python 2.5 removing support for it might be delayed an additional release).&lt;br /&gt;&lt;br /&gt;At the end of this process Django is going to end up only supporting Python 2.6.  At this point (or maybe even before), a strategy will need to be devised for how to actually handle the switch.  Some possibilities are, 1) having an official breakpoint, only one version is supported at a given time, 2) Python 3 support begins in a branch that tracks trunk and eventually it switches to become trunk once Python 3 is the more common deployment, 3) Python 2.6 and 3 are supported from a single codebase.  I'm not sure which one of these is easiest, other projects such as PLY have chosen to go with option 3, however my inclination is that option 2 will be best for Django since issues like bytes vs. string are particularly prominent in Django (since it talks to so many external data sources).&lt;br /&gt;&lt;br /&gt;For people who are interested Martin von Löwis actually put together &lt;a href="http://wiki.python.org/moin/PortingDjangoTo3k"&gt;a patch&lt;/a&gt; that, at the time, gave Django Python 3 support (at least enough to run the tutorial under SQLite).  If you're very interested in Django on Python 3 the best path would probably be to bring that patch up to date (unless it's wildly out of date, I haven't checked), and starting to fix new things that have been introduced since the patch was written.  This work isn't likely to get any official support, since maintaining Python 2.4 support and Python 3 would be far too difficult, however there's no reason you can't maintain the patch externally on something like Github or Bitbucket.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-766919506865200259?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/766919506865200259/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/django-and-python-3.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/766919506865200259'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/766919506865200259'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/django-and-python-3.html' title='Django and Python 3'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5113109010549810812</id><published>2009-11-27T01:18:00.001-05:00</published><updated>2009-11-27T01:18:45.192-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='gsoc'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Why Meta.using was removed</title><content type='html'>&lt;div&gt;Recently Russell Keith-Magee and I decided that the Meta.using option needed to be removed from the multiple-db work on Django, and so we did.  Yesterday someone tweeted that this change caught them off guard, so I wanted to provide a bit of explanation as to why we made that change.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first thing to note is that Meta.using was very good for one specific use case, horizontal partitioning by model.  Meta.using allowed you to tie a specific model to a specific database by default.  This meant that if you wanted to do things like have users be in one db and votes in another this was basically trivial.  Making this use case this simple was definitely a good thing.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The downside was that this solution was very poorly designed, particularly in light on Django's reusable application philosophy.  Django emphasizes the reusability of application, and having the Meta.using option tied your partitioning logic to your models, it also meant that if you wanted to partition a reusable application onto another DB this easily the solution was to go in and edit the source for the reusable application.  Because of this we had to go in search of a better solution.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The better solution we've come up with is having some sort of callback you can define that lets you decide what database each query should be executed on.  This would let you do simple things like direct all queries on a given model to a specific database, as well as more complex sharding logic like sending queries to the right database depending on which primary key value the lookup is by.  We haven't figured out the exact API for this, and as such this probably won't land in time for 1.2, however it's better to have the right solution that has to wait than to implement a bad API that would become deprecated in the very next release.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5113109010549810812?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5113109010549810812/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/why-metausing-was-removed.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5113109010549810812'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5113109010549810812'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/why-metausing-was-removed.html' title='Why Meta.using was removed'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8963218783413149816</id><published>2009-11-26T01:08:00.002-05:00</published><updated>2009-11-26T01:13:33.872-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>Just a Small Update</title><content type='html'>Unfortunately, I don't have an interesting, contentful post today.  Just a small update about this blog instead.  I now have a small widget on the right hand side where you can enter topics you'd like to hear about.  I don't always have a good idea of what readers are interested in, and far too often I reject blog post ideas because I think either, "no one cares about that" or "everyone always knows that" so hopefully this will be both a good way for me to write interesting content that people want to read about, as well as a good way for me to overcome any writers block.  So please submit anything you'd like to hear about, Python, Django, the web, programming in general, compilers, or me ranting about politics, I'm willing to consider any topic.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To my American readers: Happy Thanksgiving!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8963218783413149816?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8963218783413149816/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/just-small-update.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8963218783413149816'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8963218783413149816'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/just-small-update.html' title='Just a Small Update'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8803758270364181163</id><published>2009-11-25T01:03:00.001-05:00</published><updated>2009-11-25T01:03:52.171-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='review'/><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Final Review of Python Essential Reference</title><content type='html'>&lt;div&gt;Disclosure: I received a free review copy of the book.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Today I finished reading the Python Essential Reference and I wanted to share my final thoughts on the book.  I'll start by saying I still agree with everything I wrote in my initial review, specifically that it's both a great resource as well as a good way to find out what you don't already know.  Reading the second half of the book there were a few things that really exemplified this for me.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The first instance of this is the chapter on concurrency.  I've done some concurrent programming with Python, but it's mostly been small scripts, a multiprocess and multithreaded web scraper for example, so I'm familiar with the basic APIs for threading and multiprocessing.  However, this chapter goes into the full details, really covering the stuff you need to know if you want to build bigger applications that leverage these techniques.  Things like shared data for processes or events and condition variables for threads and the kind of things that the book gives a good explanation of, as well as good examples of how to use them.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The other chapter that really stood out for me is the one on network programming and sockets.  This chapter describes everything from the low-level select module up through through the included socket servers.  The most valuable part is an example of how to build an asynchronous IO system.  This example is about 2 pages long and it's a brilliant example of how to use the modules, how to make an asynchronous API feel natural, and what the tradeoffs of asynchronous versus concurrency are.  In addition, in the wake of the "* in Unix" posts from a while ago I found the section on the socket module interesting as it's something I've never actually worked directly with.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The rest of the book is a handy reference, but for me these two chapters are the types of things that earns this a place on my bookshelf.  The way Python Essential Reference balances depth with conciseness is excellent, it shows you the big picture for everything and gives you super details on the things that are really important.  I just got my review copy of Dive into Python 3 today, so I look forward to giving a review of it in the coming days.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8803758270364181163?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8803758270364181163/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/final-review-of-python-essential.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8803758270364181163'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8803758270364181163'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/final-review-of-python-essential.html' title='Final Review of Python Essential Reference'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6859642240640310330</id><published>2009-11-24T02:21:00.001-05:00</published><updated>2009-11-24T02:23:05.290-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='software'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Filing a Good Ticket</title><content type='html'>I read just about every single ticket that's filed in Django's trac, and at this point I'e gotten a pretty good sense of what (subjectively) makes a useful ticket.  Specifically there are a few things that can make your ticket no better than spam, and a few that can instantly bump your ticket to the top of my "TODO" list.  Hopefully, these will be helpful in both filing ticket's for Django as well as other open source projects.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Search for a ticket before filing a new one.  Django's trac, for example, has at least 10 tickets describing "Decoupling urls in the tutorial, part 3".  These have all been wontfixed (or closed as a duplicate of one of the others).  Each time one of these is filed it takes time for someone to read through it, write up an appropriate closing message, and close it.  Of course, the creator of the ticket also invested time in filing the ticket.  Unfortunately, for both parties this is time that could be better spent doing just about anything else, as the ticket has been decisively dealt with plenty of times.&lt;/li&gt;&lt;li&gt;On a related note, please don't reopen a ticket that's been closed before.  This one depends more on the policy of the project, in Django's case the policy is that once a ticket has been closed by a core developer the appropriate next step is to start a discussion on the development mailing list.  Again this results in some wasted time for everyone, which sucks.&lt;/li&gt;&lt;li&gt;Read the contributing documentation.  Not every project has something like this, but when a project does it's definitely the right starting point.  It will hopefully contain useful general bits of knowledge (like what I'm trying to put here) as well as project specific details, what the processes are, how to dispute a decision, how to check the status of a patch, etc.&lt;/li&gt;&lt;li&gt;Provide a minimal test case.  If I see a ticket who's description involves a 30 field model, it drops a few rungs on my TODO list.  Large blocks of code like this take more time to wrap ones head around, and most of it will be superfluous.  If I see just a few lines of code it takes way less time to understand, and it will be easier to spot the origin of the problem.  As an extension to this if the test case comes in the form of a patch to Django's test suite it becomes even easier for a developer to dive into the problem.&lt;/li&gt;&lt;li&gt;Don't file a ticket advocating a major feature or sweeping change.  Pretty much if it's going to require a discussion the right place to start is the mailing list.  Trac is lousy at facilitating discussions, mailing lists are designed explicitly for that purpose.  A discussion on the mailing list can more clearly outline what needs to happen, and it may turn out that several tickets are needed.  For example filing a ticket saying, "Add CouchDB support to the ORM" is pretty useless, this requires a huge amount of underlying changes to make it even possible, and after that a database backend can live external to Django, so there's plenty of design decisions to go around.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;These are some of the issues I've found to be most pressing while reviewing tickets for Django.  I realize they are mostly in the "don't" category, but filing a good ticket can sometimes be as good as clearly stating what the problem is, and how to reproduce it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6859642240640310330?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6859642240640310330/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/filing-good-ticket.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6859642240640310330'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6859642240640310330'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/filing-good-ticket.html' title='Filing a Good Ticket'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2972676144212484311</id><published>2009-11-23T00:55:00.001-05:00</published><updated>2009-11-23T00:57:12.871-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='yacc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='lex'/><category scheme='http://www.blogger.com/atom/ns#' term='ply'/><category scheme='http://www.blogger.com/atom/ns#' term='parse'/><title type='text'>Using PLY for Parsing Without Using it for Lexing</title><content type='html'>Over the past week or so I've been struggling with attempting to write my own parser (or parser generator) by hand.  A few days ago I finally decided to give up on this notion (after all the parser isn't my end goal) as it was draining my time from the interesting work to be done.  However, I wanted to keep my existing lexer.  I wrote the lexer by hand in the method I described in a previous post, it's fast, easy to read, and I rather like my handiwork, so I wanted to keep it if possible.  I've used PLY before (as I described last year) so I set out to see if it would be possible to use it for parsing without using it for lexing.&lt;br /&gt;&lt;br /&gt;As it turns out PLY expects only a very minimal interface from it's lexer.  In fact it only needs one method, token(), which returns a new token (or None at the end).  Tokens are expected to have just 4 attributes.  Having this knowledge I now set out to write a pair of compatibility classes for my existing lexer and token classes, I wanted to do this without altering the lexer/token API so that if and when I finally write my own parser I don't have to remove legacy compatibility stuff.  My compatibility classes are very small, just this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    class PLYCompatLexer(object):&lt;br /&gt;        def __init__(self, text):&lt;br /&gt;            self.text = text&lt;br /&gt;            self.token_stream = Lexer(text).parse()&lt;br /&gt;        &lt;br /&gt;        def token(self):&lt;br /&gt;            try:&lt;br /&gt;                return PLYCompatToken(self.token_stream.next())&lt;br /&gt;            except StopIteration:&lt;br /&gt;                return None&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;    class PLYCompatToken(object):&lt;br /&gt;        def __init__(self, token):&lt;br /&gt;            self.type = token.name&lt;br /&gt;            self.value = token.value&lt;br /&gt;            self.lineno = None&lt;br /&gt;            self.lexpos = None&lt;br /&gt;        &lt;br /&gt;        def __repr__(self):&lt;br /&gt;            return "&amp;lt;Token: %r %r&amp;gt;" % (self.type, self.value)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is the entirety of the API that PLY needs.  Now I can write my parser exactly as I would normally with PLY.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2972676144212484311?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2972676144212484311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/using-ply-for-parsing-without-using-it.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2972676144212484311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2972676144212484311'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/using-ply-for-parsing-without-using-it.html' title='Using PLY for Parsing Without Using it for Lexing'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3233101122886270884</id><published>2009-11-22T00:43:00.001-05:00</published><updated>2009-11-22T00:45:02.160-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming-languages'/><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='pypy'/><title type='text'>A Bit of Benchmarking</title><content type='html'>PyPy recently posted some &lt;a href="http://morepypy.blogspot.com/2009/11/some-benchmarking.html"&gt;interesting benchmarks&lt;/a&gt; from the computer language shootout, and in my last post about Unladen Swallow I described a patch that would hopefully be landing soon.  I decided it would be interesting to benchmarks something with this patch.  For this I used James Tauber's &lt;a href="http://github.com/jtauber/mandelbulb"&gt;Mandelbulb&lt;/a&gt; application, at both 100x100 and 200x200.  I tested CPython, Unladen Swallow Trunk, Unladen Swallow Trunk with the patch, and a recent PyPy trunk (compiled with the JIT).  My results were as follows:&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;100&lt;br /&gt;&lt;/span&gt;CPython 2.6.4                   17s&lt;br /&gt;Unladen Swallow Trunk           16s&lt;br /&gt;Unladen Swallow Trunk + Patch   13s&lt;br /&gt;PyPy Trunk                      10s&lt;br /&gt;&lt;br /&gt;&lt;span class="Apple-style-span" style="font-size: large;"&gt;200&lt;br /&gt;&lt;/span&gt;CPython 2.6.4                   64s&lt;br /&gt;Unladen Swallow Trunk           52s&lt;br /&gt;Unladen Swallow Trunk + Patch   49s&lt;br /&gt;PyPy                            46s&lt;br /&gt;&lt;br /&gt;Interesting results.  At 100x100 PyPy smokes everything else, and the patch shows a clear benefit for Unladen.  However, at 200x200 both PyPy and the patch show diminishing returns.  I'm not clear on why this is, but my guess is that something about the increased size causes a change in the parameters that makes the generated code less efficient for some reason.&lt;br /&gt;&lt;br /&gt;It's important to note that Unladen Swallow has been far less focussed on numeric benchmarks than PyPy, instead focusing on more web app concerns (like template languages).  I plan to benchmark some of these as time goes on, particularly after PyPy merges their "faster-raise" branch, which I'm told improves PyPy's performance on Django's template language dramatically.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3233101122886270884?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3233101122886270884/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/bit-of-benchmarking.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3233101122886270884'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3233101122886270884'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/bit-of-benchmarking.html' title='A Bit of Benchmarking'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8005726886588467982</id><published>2009-11-21T00:02:00.001-05:00</published><updated>2009-11-21T00:03:59.677-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='response'/><category scheme='http://www.blogger.com/atom/ns#' term='lex'/><category scheme='http://www.blogger.com/atom/ns#' term='pypy'/><category scheme='http://www.blogger.com/atom/ns#' term='parse'/><category scheme='http://www.blogger.com/atom/ns#' term='yacc'/><category scheme='http://www.blogger.com/atom/ns#' term='college'/><category scheme='http://www.blogger.com/atom/ns#' term='programming-languages'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><category scheme='http://www.blogger.com/atom/ns#' term='ply'/><category scheme='http://www.blogger.com/atom/ns#' term='unladen-swallow'/><title type='text'>Things College Taught me that the "Real World" Didn't</title><content type='html'>&lt;div&gt;A while ago Eric Holscher blogged about &lt;a href="http://ericholscher.com/blog/2009/nov/10/what-they-didnt-teach-me-college/"&gt;things he didn't learn in college.&lt;/a&gt;  I'm going to take a different spin on it, looking at both things that I did learn in school that I wouldn't have learned else where (henceforth defined as my job, or open source programming), as well as thinks I learned else where instead of at college.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Things I learned in college:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Big O notation, and algorithm analysis.  This is the biggest one, I've had little cause to consider this in my open source or professional work, stuff is either fast or slow and that's usually enough.  Learning rigorous algorithm analysis doesn't come up all the time, but every once in a while it pops up, and it's handy.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;C++.  I imagine that I eventually would have learned it myself, but my impetus to learn it was that's what was used for my CS2 class, so I started learning with the class then dove in head first.  Left to my own devices I may very well have stayed in Python/Javascript land.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Finite automaton and push down automaton.  I actually did lexing and parsing before I ever started looking at these in class (see my blog posts from a year ago) using PLY, however, this semester I've actually been learning about the implementation of these things (although sadly for class projects we've been using Lex/Yacc).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;span class="Apple-style-span" style="font-size: x-large;"&gt;Things I learned in the real world:&lt;/span&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Compilers.  I've learned everything I know about compilers from reading my papers from my own interest and hanging around communities like Unladen Swallow and PyPy (and even contributing a little).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Scalability.  Interesting this is a concept related to algorithm analysis/big O, however this is something I've really learned from talking about this stuff with guys like Mike Malone and Joe Stump.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;APIs, Documentation.  These are the core of software development (in my opinion), and I've definitely learned these skills in the open source world.  You don't know what a good API or documentation is until it's been used by someone you've never met and it just works for them, and they can understand it perfectly.  One of the few required, advanced courses at my school is titled, "Software Design and Documentation" and I'm deathly afraid it's going to waste my time with stuff like UML, instead of focusing on how to write APIs that people want to use and documentation that people want to read.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So these are my short lists.  I've tried to highlight items that cross the boundaries between what people traditionally expect are topics for school and topics for the real world.  I'd be curious to hear what other people's experience with topics like these are.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8005726886588467982?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8005726886588467982/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/things-college-taught-me-that-real.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8005726886588467982'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8005726886588467982'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/things-college-taught-me-that-real.html' title='Things College Taught me that the &quot;Real World&quot; Didn&apos;t'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5573208700261687615</id><published>2009-11-19T23:50:00.000-05:00</published><updated>2009-11-19T23:51:15.715-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming-languages'/><category scheme='http://www.blogger.com/atom/ns#' term='pypy'/><category scheme='http://www.blogger.com/atom/ns#' term='unladen-swallow'/><title type='text'>Another Pair of Unladen Swallow Optimizations</title><content type='html'>Today a &lt;a href="http://code.google.com/p/unladen-swallow/source/detail?r=904"&gt;patch of mine&lt;/a&gt; was committed to Unladen Swallow.  In the past weeks I've described some of the optimizations that have gone into Unladen Swallow, in specific I looked at removing the allocation of an argument tuple for C functions.  One of the "on the horizon" things I mentioned was extending this to functions with a variable arity (that is the number of arguments they take can change).  This has been implemented for functions that take a finite range of argument numbers (that is, they don't take *args, they just have a few arguments with defaults).  This support was used to optimize a number of builtin functions (dict.get, list.pop, getattr for example).&lt;br /&gt;&lt;br /&gt;However, there were still a number of functions that weren't updated for this support.  I initially started porting any functions I saw, but it wasn't a totally mechanical translation so I decided to do a little profiling to better direct my efforts.  I started by using the cProfile module to see what functions were called most frequently in Unladen Swallow's Django template benchmark.  Imagine my surprise when I saw that unicode.encode was called over 300,000 times!  A quick look at that function showed that it was a perfect contender for this optimization, it was currently designated as a METH_VARARGS, but in fact it's argument count was a finite range.  After about of dozen lines of code, to change the argument parsing, I ran the benchmark again, comparing it a control version of Unladen Swallow, and it showed a consistent 3-6% speedup on the Django benchmark.  Not bad for 30 minutes of work.&lt;br /&gt;&lt;br /&gt;Another optimization I want to look at, which hasn't landed yet, is one of optimize various operations.  Right now Unladen Swallow tracks various data about the types seen in the interpreter loop, however for various operators this data isn't actually used.  What this patch does is check at JIT compilation time whether the operator site is monomorphic (that is there is only one pair of types ever seen there), and if it is, and it is one of a few pairings that we have optimizations for (int + int, list[int], float - float for example) then optimized code is emitted.  This optimized code checks the types of both the arguments that they are the expected ones, if they are then the optimized code is executed, otherwise the VM bails back to the interpreter (various literature has shown that a single compiled optimized path is better than compiling both the fast and slow paths).  For simple algorithm code this optimization can show huge improvements.&lt;br /&gt;&lt;br /&gt;The PyPy project has &lt;a href="http://morepypy.blogspot.com/2009/11/some-benchmarking.html"&gt;recently blogged&lt;/a&gt; about the results of the results of some benchmarks from the Computer Language Shootout run on PyPy, Unladen Swallow, and CPython.  In these benchmarks Unladen Swallow showed that for highly algorithmic code (read: mathy) it could use some work, hopefully patches like this can help improve the situation markedly.  Once this patch lands I'm going to rerun these benchmarks to see how Unladen Swallow improves, I'm also going to add in some of the more macro benchmarks Unladen Swallow uses to see how it compares with PyPy in those.  Either way, seeing the tremendous improvements PyPy and Unladen Swallow have over CPython gives me tremendous hope for the future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5573208700261687615?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5573208700261687615/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/another-pair-of-unladen-swallow.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5573208700261687615'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5573208700261687615'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/another-pair-of-unladen-swallow.html' title='Another Pair of Unladen Swallow Optimizations'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7361063219555483600</id><published>2009-11-19T00:07:00.001-05:00</published><updated>2009-11-19T00:07:52.234-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='admin'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Announcing django-admin-histograms</title><content type='html'>This is just a quick post because it's already past midnight here.  Last week I realized some potentially useful code that I extracted from the DjangoDose and typewar codebases.  Basically this code let's you get simple histogram reports for models in the admin, grouped by a date field.  After I released it David Cramer did some work to make the code slightly more flexible, and to provide a generic templatetag for creating these histograms anywhere.  The code can be found on &lt;a href="http://github.com/alex/django-admin-histograms"&gt;github&lt;/a&gt;, and if you're curious what it looks like there's a &lt;a href="http://wiki.github.com/alex/django-admin-histograms"&gt;screenshot on the wiki&lt;/a&gt;.  Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7361063219555483600?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7361063219555483600/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/announcing-django-admin-histograms.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7361063219555483600'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7361063219555483600'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/announcing-django-admin-histograms.html' title='Announcing django-admin-histograms'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-433083982238902488</id><published>2009-11-17T23:32:00.002-05:00</published><updated>2009-11-18T00:39:50.470-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='programming-languages'/><category scheme='http://www.blogger.com/atom/ns#' term='lex'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><category scheme='http://www.blogger.com/atom/ns#' term='parse'/><title type='text'>Writing a Lexer</title><content type='html'>People who have been reading this blog since last year (good lord) may recall that once upon a time I did a short series of posts on lexing and parsing using PLY.  Back then I was working on a language named Al.  This past week or so I've started working on another personal project (not public yet) and I've once again had the need to lex things, but this time I wrote my lexer by hand, instead of using any sort of generator.  This has been an exceptional learning experience, so I'd like to pass some of that on to you.&lt;br /&gt;&lt;br /&gt;The first thing to note is that writing a lexer is a great place to TDD (test driven development), I've rewritten various parts of my lexer five or more times, I've needed my tests to keep me sane.  Got your tests written?  Ok it's time to dive right into our lexer.&lt;br /&gt;&lt;br /&gt;I've structured my lexer as a single class that takes an input string, and has a parse method which returns a generator that yields tokens (tokens are just a namedtuple with a name and value field).  The parser has two important attributes, state which is a string that says what state the lexer is in (this is used for tokens that are more than one character long), and current_val which is a list containing characters that will eventually become the value for the current token being found.&lt;br /&gt;&lt;br /&gt;The parse method iterates through characters in the text and then it checks, if the parser has a state (self.state is not None) it does getattr(self, self.state)(character).  Otherwise it calls self.generic(character).  Then the various "state methods" are responsible for mutating self.current_val and self.state and returning a Token.  So for example the string state looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def string(self, ch):&lt;br /&gt;        if ch == '"':&lt;br /&gt;            sym = Symbol("STRING", "".join(self.current_val))&lt;br /&gt;            self.current_val = []&lt;br /&gt;            self.state = None&lt;br /&gt;            return sym&lt;br /&gt;        elif ch == "\\":&lt;br /&gt;            self.state = "string_escaped"&lt;br /&gt;        else:&lt;br /&gt;            self.current_val.append(ch)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;If the character is a quote then we're closing our string so we return our string Symbol, reset the current_val and state.  If the character is a \ then we switch into a string_escaped state which knows to handle the character as a literal and then go back to string state.  If the character is anything else then we just append it to the current_val, it will get handled at the end of the string.&lt;br /&gt;&lt;br /&gt;I've found this to be an exceptionally powerful method, and it makes my end result code very readable.  Hopefully I'll be able to reveal my project in the coming weeks, as I'm very excited about it, even if it's not ready I'll continue to share these lessons learned as I go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-433083982238902488?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/433083982238902488/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/writing-lexer.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/433083982238902488'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/433083982238902488'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/writing-lexer.html' title='Writing a Lexer'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-4961457737828047508</id><published>2009-11-16T23:01:00.001-05:00</published><updated>2009-11-16T23:03:21.712-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>My Next Blog</title><content type='html'>&lt;div&gt;I've been using this Blogspot powered blog for over a year now, and it is starting to get a little long in the tooth.  Therefore I've been planning on moving to a new, shinny, blog of my own in the coming months.  Specifically it's going to be my goal to get myself 100% migrated over my winter break.  Therefore I've started building myself a list of features I want:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ul&gt;&lt;li&gt;Able to migrate all my old posts.  This isn't going to be a huge deal, but it has to happen as I have quite a few posts I don't want to lose.&lt;/li&gt;&lt;li&gt;Accepts restructured text.  I'm sick of writing my posts in reST and then converting it into HTML for Blogspot.&lt;/li&gt;&lt;li&gt;Pretty code highlighting.&lt;/li&gt;&lt;li&gt;Disqus for comments.  I don't want to have to deal with spam or anything else, let users post with Disqus and they can deal with all the trouble.&lt;/li&gt;&lt;li&gt;Looks decent.  Design is a huge weak point for me, so most of my time is going to be dedicated to this I think.&lt;/li&gt;&lt;li&gt;Django powered!&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div&gt;That's my pony list thus far.  There's a good bet I'll use django-mingus since I've hear such good things about it, but for now I'm just dreaming of being able to write these posts in reST.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-4961457737828047508?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/4961457737828047508/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/my-next-blog.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4961457737828047508'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4961457737828047508'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/my-next-blog.html' title='My Next Blog'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2103851649657982890</id><published>2009-11-15T22:25:00.002-05:00</published><updated>2009-11-15T22:43:56.645-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='review'/><category scheme='http://www.blogger.com/atom/ns#' term='book'/><title type='text'>Initial Review: Python Essential Reference</title><content type='html'>Disclosure: I received a free review copy of Python Essential Reference, Fourth Edition.&lt;br /&gt;&lt;br /&gt;I've never really used reference material, I've always loved tutorials, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;howtos&lt;/span&gt;, and guides for learning things, but I've usually shunned reference material in favor of reading the source.  Therefore, I didn't think I'd have a huge use for this book.  However, so far (I've read about half the book so far) I've found it to be an exceptional resource, and I definitely plan on keeping it on my bookshelf.&lt;br /&gt;&lt;br /&gt;The first third or so of the book is a reference on the syntax and other basic constructs of Python, it's probably not the part of the book you'll be consulting very frequently if you're an experienced Python programmer, however the end of this section is a bit of "Testing, Debugging, Profiling, and Tuning", this I can see myself flipping back to, as it extensively documents the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;doctests&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;unittests&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;pdb&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;cProfile&lt;/span&gt;, and dis modules.&lt;br /&gt;&lt;br /&gt;The next third of the book is all about the Python library, including both the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;builtins&lt;/span&gt; and the standard library.  This section is organized by functionality and I can definitely see myself using it.  For example it has sections on "Python &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;Runtime&lt;/span&gt; Services" (like &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;atexit&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;gc&lt;/span&gt;, marshal, and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;weakref&lt;/span&gt;), "Data Structures, Algorithms, and Code Simplification" (bisect, collections, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;heapq&lt;/span&gt; for example), "String and Text Handling" (&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;codecs&lt;/span&gt;, re, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;struct&lt;/span&gt;), and "Python Database Access" (PEP249, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_13"&gt;sqlite&lt;/span&gt;, and &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_14"&gt;dbm&lt;/span&gt;).  There's more, but this is as far as I've read.  Reading through like a novel each of these sections has exposed me to things I wasn't aware of or don't use as frequently as I should, and I plan on using this book as a resource for exploring them.  David &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15"&gt;Beazley&lt;/span&gt; has painstakingly documented the details of these modules, paying particular attention to the functions and classes you are likely to need most.&lt;br /&gt;&lt;br /&gt;All in all I've found the Python Essential Reference to be a good book, especially for people who like reference documentation.  Depending on how you use Python this book can serve as an excellent eye opener into other parts of the language and standard library, and for me I think that's where a ton of value will come from, as a day to day Python user I don't need a reference for most of the language, but for the bits it's introducing me to, having it handy will be a leg up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2103851649657982890?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2103851649657982890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/initial-review-python-essential.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2103851649657982890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2103851649657982890'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/initial-review-python-essential.html' title='Initial Review: Python Essential Reference'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3665442302992195933</id><published>2009-11-14T23:37:00.001-05:00</published><updated>2009-11-14T23:38:44.768-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='admin'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='gsoc'/><category scheme='http://www.blogger.com/atom/ns#' term='jquery'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Why jQuery shouldn't be in the admin</title><content type='html'>This summer as a part of the Google Summer of Code program Zain Memon worked on improving the UI for Django's admin, specifically he integrated jQuery for various interface improvements.  I am opposed to including jQuery in Django's admin, as far as I know I'm the only one.  I should note that on a personal level I love jQuery, however I don't think that means it should be included in Django proper.  I'm going to try to explain why I think it's a bad idea and possibly even convince you.&lt;br /&gt;&lt;br /&gt;The primary reason I'm opposed is because it lowers the pool of people who can contribute to developing Django's admin.  I can hear the shouts from the audience, "jQuery makes Javascript easy, how can it LOWER the pool".  By using jQuery we prevent people who know Javascript, but not jQuery from contributing to Django's admin.  If we use more "raw" Javascript then anyone who knows jQuery should be able to contribute, as well as anyone who knows Mootools, or Dojo, or just vanilla Javascript.  I'm sure there are some people who will say, "but it's possible to use jQuery without knowing Javascript", I submit to you that this is a bad thing and certainly shouldn't be encouraged.  We need to look no further than Jacob Kaplan-Moss's talks on Django where he speaks of his concern at job postings that look for Django experience with no mention of Python.&lt;br /&gt;&lt;br /&gt;The other reason I'm opposed is because selecting jQuery for the admin gives the impression that Django has a blessed Javascript toolkit.  I'm normally one to say, "if people make incorrect inferences that's their own damned problem," however in this case I think they would be 100% correct, Django would have blessed a Javascript toolkit.  Once again I can hear the calls, "But, it's in contrib, not Django core", and again I disagree, Django's contrib isn't like other projects' contrib directories that are just a dumping ground for random user contributed scripts and other half working features.  Django's contrib is every bit as official as parts of Django that live elsewhere in the source tree.  Jacob Kaplan-Moss has described &lt;a href="http://jacobian.org/writing/what-is-django-contrib/"&gt;what django.contrib is&lt;/a&gt;, no part of that description involves it being less official, quite the opposite in fact.&lt;br /&gt;&lt;br /&gt;For these reasons I believe Django's admin should avoid selecting a Javascript toolkit, and instead maintain it's own handrolled code.  Though this brings an increase burden on developers I believe it is more important to these philosophies than to take small development wins.  People saying this stymies the admin's development should note that Django's admin's UI has changed only minimally over the past years, and only a small fraction of that can be attributed to difficulties in Javascript development.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3665442302992195933?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3665442302992195933/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/why-jquery-shouldnt-be-in-admin.html#comment-form' title='52 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3665442302992195933'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3665442302992195933'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/why-jquery-shouldnt-be-in-admin.html' title='Why jQuery shouldn&apos;t be in the admin'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>52</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8857336294206045208</id><published>2009-11-13T23:31:00.000-05:00</published><updated>2009-11-13T23:32:12.086-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='response'/><category scheme='http://www.blogger.com/atom/ns#' term='programming-languages'/><category scheme='http://www.blogger.com/atom/ns#' term='go'/><title type='text'>Syntax Matters</title><content type='html'>&lt;div&gt;Yesterday I wrote about why I wasn't very interested in Go.  Two of my three major complaints were about the syntax of Go, and based on the comments I got here and on Hacker News a lot of people didn't seem to mind the syntax, or at least didn't think it was worth talking about.  However, the opposite is true, for me the syntax is among the single most important things about a programming language.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I'd estimate that I spend about 60% of my day thinking about and reading code and 40% actually writing code.  This means that code needs to be easy to read, that means no stray punctuation or anything else that distracts me from what I want to see in my code: what does it do when I run it.  This means any code I'm looking at better be properly indented.  It also means that I find braces and semicolons to be noise, stuff that just distracts me from what I'm reading the code to do.  Therefore, code ought to use the existing, nonintrusive, structure, instead of obligating me to add more noise.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;"Programs must be written for people to read, and only incidentally for machines to execute."  This is a quote from Structure and Interpretation of Computer Programs, by Harold Abelson and Gerald Sussman.  It has always struck me as odd that the people who wrote that chose to use Scheme for their text book.  In my view Lisp and Scheme are the height of writing for a machine to execute.  I think David Heinemeier Hansson got it right when he said, "code should be beautiful", I spent 5+ hours a day reading it, I damned well better want to look at it.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8857336294206045208?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8857336294206045208/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/syntax-matters.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8857336294206045208'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8857336294206045208'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/syntax-matters.html' title='Syntax Matters'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2414198058238415688</id><published>2009-11-12T21:28:00.000-05:00</published><updated>2009-11-12T21:29:12.227-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='programming-languages'/><category scheme='http://www.blogger.com/atom/ns#' term='go'/><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><title type='text'>Why I'm not very excited about Go</title><content type='html'>When I first heard Rob Pike and Ken Thompson had created a new programming language my first instinct is, "I'm sure it'll be cool, these are brilliant guys!"  Unfortunately that is not the case, mostly because I think they made some bad design choices, and because the "cool new things" aren't actually that new, or cool.&lt;br /&gt;&lt;br /&gt;The first major mistake was using a C derived syntax: braces and semicolons.  I know some people don't like significant whitespace, but if you aren't properly indenting your code it's already beyond the point of hopelessness.  The parser ought to use the existing structure instead of adding more punctuation.  Readability is one of the most important attributes of code, and this syntax detracts from that.&lt;br /&gt;&lt;br /&gt;The next mistake is having a separate declaration and assignment operator.  I understand that the point of this operator is to reduce the repetition of typing out the types name both in declaring the variable and in initializing the value.  Yet it seems the purpose of the := operator is to avoid typos in variable names that are possible by making all assignment an implicit declaration if the variable wasn't already declared.  I can see myself making many more typos by forgetting to use the := operator, and in cases where I make a typo in a variable name it would inevitably be caught by the compiler when I attempted to actually use it (the fact that this is an attempted declaration means the variable won't have been declared elsewhere).&lt;br /&gt;&lt;br /&gt;The final mistake was not providing generics.  C++'s templates is one of the things that make the language head and shoulders more useful for me than C for tasks I need to preform; generics allow one to provide reusable data structures.  While Go seems to have something akin to generics with their map data structure, it disappointingly doesn't appear to be exposed in any way to user code.  One of the things I've found makes me most productive in Python is that any time I need to perform a task I simply pick the data structure that does what I want and it is efficiently implemented.  Without generics, I don't see a way for a statically typed language to offer this same breadth of data structures without each of them being a special case.&lt;br /&gt;&lt;br /&gt;In terms of features which I believe are overhyped the most important one is the "goroutine".  As best I can tell these are an implementation of &lt;a href="http://en.wikipedia.org/wiki/Fiber_(computer_science)"&gt;fibers&lt;/a&gt;.  Constructs like concurrency should not be granted their own syntax, especially when they can be cleanly implemented using the other constructs of a language, look at the C library &lt;a href="http://swtch.com/libtask/"&gt;libtask&lt;/a&gt; as an example.&lt;br /&gt;&lt;br /&gt;Further, the handling of interfaces, though interesting, appears to be an implementation of C++0x's proposed concepts (which won't be in the final standard).  I view this feature as something that is most useful in the context of generics, which Go doesn't have.  The reason for this is to be able to make compile time assertions about the types that are being templated over.  Doing anything else is more clearly implemented as abstract inheritance.&lt;br /&gt;&lt;br /&gt;I'm not writing off Go permanently, but I'm not enthused about it, someone will probably have to let me know when I need to look again as I won't be following along.  Enjoy your internet travels.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2414198058238415688?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2414198058238415688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/why-im-not-very-excited-about-go.html#comment-form' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2414198058238415688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2414198058238415688'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/why-im-not-very-excited-about-go.html' title='Why I&apos;m not very excited about Go'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7288598771075171394</id><published>2009-11-11T20:56:00.001-05:00</published><updated>2009-11-11T20:58:11.289-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='response'/><category scheme='http://www.blogger.com/atom/ns#' term='rails'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>When Django Fails? (A response)</title><content type='html'>I saw &lt;a href="http://zef.me/2308/when-rails-fails"&gt;an article&lt;/a&gt; on reddit (or was in hacker news?) that asked the question: what happens when newbies make typos following the Rails tutorial, and how good of a job does Rails do at giving useful error messages?  I decided it would be interesting to apply this same question to Django, and see what the results are.  I didn't have the time to review the entire Django tutorial, so instead I'm going to make the same mistakes the author of that article did and see what the results are, I've only done the first few where the analogs in Django were clear.&lt;br /&gt;&lt;br /&gt;Mistake #1: Point a URL at a non-existent view:&lt;br /&gt;&lt;br /&gt;I pointed a URL at the view "django_fails.views.homme" when it should have been "home".  Let's see what the error is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    ViewDoesNotExist at /&lt;br /&gt;    Tried homme in module django_fails.views. Error was: 'module' object has no attribute 'homme'&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the exception name is definitely a good start, combined with the error text I think it's pretty clear that the view doesn't exist.&lt;br /&gt;&lt;br /&gt;Mistake #2: misspell url in the mapping file&lt;br /&gt;&lt;br /&gt;Instead of doing url("^$" ...) I did urll:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    NameError at /&lt;br /&gt;    name 'urll' is not defined&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The error is a normal Python exception, which for a Python programmer is probably decently helpful, the cake is that if you look at the traceback it points to the exact line, in user code, that has the typo, which is exactly what you need.&lt;br /&gt;&lt;br /&gt;Mistake #3: Linking to non-existent pages&lt;br /&gt;&lt;br /&gt;I created a template and tried to use the {% url %} tag on a nonexistent view.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    TemplateSyntaxError at /&lt;br /&gt;    Caught an exception while rendering: Reverse for 'homme' with arguments '()' and keyword arguments '{}' not found.&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It points me at the exact line of the template that's giving me the error and it says that the reverse wasn't found, it seems pretty clear to me, but it's been a while since I was new, so perhaps a new users perspective on an error like this would be important.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;It seems clear to me that Django does a pretty good job with providing useful exceptions, in particular the tracebacks on template specific exceptions can show you where in your templates the errors are.  One issue I'll note that I've experience in my own work is that when you have an exception from within a templatetag it's hard to get the Python level traceback, which is important when you are debugging your own templatetags.  However, there's a ticket that's been filed for that in Django's trac.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7288598771075171394?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7288598771075171394/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/when-django-fails-response.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7288598771075171394'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7288598771075171394'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/when-django-fails-response.html' title='When Django Fails? (A response)'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5729996680743630677</id><published>2009-11-10T21:06:00.001-05:00</published><updated>2009-11-10T21:07:32.963-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='gsoc'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>The State of MultiDB (in Django)</title><content type='html'>As you, the reader, may know this summer I worked for the Django Software Foundation via the Google Summer of Code program.  My task was to implement multiple database support for Django.  Assisting me in this task were my mentors Russell Keith-Magee and Nicolas Lara (you may recognize them as the people responsible for aggregates in Django).  By the standards of the Google Summer of Code project my work was considered a success, however, it's not yet merged into Django's trunk, so I'm going to outline what happened, and what needs to happen before this work is considered complete.&lt;br /&gt;&lt;br /&gt;Most of the major things happened, settings were changed from a series of DATABASE_* to a DATABASES setting that's keyed by DB aliases and who's values are dictionaries containing the usual DATABASE* options, QuerySets grew a using() method which takes a DB alias and says what DB the QuerySet should be evaluated against, save() and delete() grew similar using keyword arguments, a using option was added to the inner Meta class for models, transaction support was expanded to include support for multiple databases, as did the testing framework.  In terms of internals almost every internal DB related function grew explicit passing of the connection or DB alias around, rather than assuming the global connection object as they used to.  As I blogged previously ManyToMany relations were completely refactored.  If it sounds like an awful lot got done, that's because it did, I knew going in that multi-db was a big project and it might not all happen within the confines of the summer.&lt;br /&gt;&lt;br /&gt;So if all of that stuff got done, what's left?  Right before the end of the GSOC time frame Russ and I decided that a fairly radical rearchitecting of the Query class (the internal datastructure that both tracks the state of an operation and forms its SQL) was needed.  Specifically the issue was that database backends come in two varieties.  One is something like a backend for App Engine, or CouchDB.  These have a totally different design than SQL, they need different datastructures to track the relevant information, and they need different code generation.  The second type of database backend is one for a SQL database.  By contrast these all share the same philosophies and basic structure, in most cases their implementation just involves changing the names of database column types or the law LIMIT/OFFSET is handled.  The problem is Django treated all the backends equally.  For SQL backends this meant that they got their own Query classes even though they only needed to overide half of the Query functionality, the SQL generation half, as the datastructure part was identical since the underlying model is the same.  What this means is that if you make a call to using() on a QuerySet half way through it's construction you need to change the class of the Query representation if you switch to a database with a different backend.  This is obviously a poor architecture since the Query class doesn't need to be changed, just the bit at the end that actually constructs the SQL.  To solve this problem Russ and I decided that the Query class should be split into two parts, a Query class that stores bits about the current query, and a SQLCompiler which generated SQL at the end of the process.  And this is the refactoring that's holding up the merger of my multi-db work primarily.&lt;br /&gt;&lt;br /&gt;This work is largely done, however the API needs to be finalized and the Oracle backend ported to the new system.  In terms of other work that needs to be done, GeoDjango needs to be shown to shown to still work (or fixed).  In my opinion everything else on the TODO list (&lt;a href="http://etherpad.com/DWIzZa2UZn"&gt;available here&lt;/a&gt;, please don't deface) is optional for multi-db to be merge ready, with the exception of more example documentation.&lt;br /&gt;&lt;br /&gt;There are already people using the multi-db branch (some even in production), so I'm confident about it's stability.  For the next 6 weeks or so (until the 1.2 feature deadline), my biggest priority is going to be getting this branch into a merge ready state.  If this is something that interests you please feel free to get in contact with me (although if you don't come bearing a patch I might tell you that I'll see you in 6 weeks ;)), if you happen to find bugs they can be filed on the Django trac, with version "soc2009/multidb".  As always contributors are welcome, you can find the absolute latest work on &lt;a href="http://github.com/alex/django"&gt;my Github&lt;/a&gt; and a relatively stable version in my &lt;a href="http://code.djangoproject.com/browser/django/branches/soc2009/multidb/"&gt;SVN branch&lt;/a&gt; (this doesn't contain the latest, in progress, refactoring).  Have fun.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5729996680743630677?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5729996680743630677/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/state-of-multidb-in-django.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5729996680743630677'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5729996680743630677'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/state-of-multidb-in-django.html' title='The State of MultiDB (in Django)'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7550501495850173090</id><published>2009-11-09T22:35:00.001-05:00</published><updated>2009-11-09T22:37:04.552-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='software'/><title type='text'>Software that deserves a thank you</title><content type='html'>Today's post was originally supposed to be about my work on multiple database support for Django, but I'm exceptionally tired so that's been bumped to tomorrow, sorry.  Instead I'm going to use today's post to give a thank you to some software I use that doesn't get enough press, and that surely deserves a thanks.  I'm not going to be listing any libraries or programming languages just the desktop software I run day to day:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Chromium - I've been super please since switching to Chromium for my day to day browser, it's very fast, but I wish it had plugin support, and a debug tool like Firebug.&lt;/li&gt;&lt;li&gt;Firefox - My development browser, the cornucopia of plugins makes my life easier, everything from Firebug to DownloadThemAll.&lt;/li&gt;&lt;li&gt;XChat - It's my IRC client, I probably log about a dozen hours on IRC per day, maybe more.  The biggest wart I have with it is that ctrl+l clears the screen, and that's not bad for software I use for 70+ hours a week.&lt;/li&gt;&lt;li&gt;Gedit - It's my text editor.  Syntax highlighting, file browser, proper indentation support, I'm not sure there's a whole lot more to ask for.&lt;/li&gt;&lt;li&gt;Skype - Between using it to record DjangoDose to catching up with friends it's an invaluable asset.&lt;/li&gt;&lt;li&gt;Ubuntu - My operating system, by extension the Linux kernel, Gnome desktop and everything else that goes into it should all take a bow.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;And that's pretty much it for desktop software, I took a peak back at my list from last year and it's almost identical, I guess all of this software is doing something right.  I also wanted to take a minute to thank various web services I use:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Pandora - I blow through my monthly allowance of 40 hours in one week.  I think that says something.&lt;/li&gt;&lt;li&gt;last.fm - I love the fact that it tracks all of the stats about the music I listen to.  I really wish it had a way to combine the "music neighborhood" and friends features to find people in my social graph with similar taste in music.&lt;/li&gt;&lt;li&gt;Github - It really is the bee's knees of code hosting software.  There's very little to say other than the number of repositories I have should stand as a testament to its quality.&lt;/li&gt;&lt;li&gt;Blogger - I use it to host this blog, and while I'm actively working towards moving away from it, for now it stays and it deserves a thank you.&lt;/li&gt;&lt;li&gt;Invoice Machine - It takes most of the tedium out of dealing with invoicing, I'm grateful for that.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;And that's my list.  I promise that tomorrow I'll have my post on multiple database support.  See you then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7550501495850173090?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7550501495850173090/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/software-that-deserves-thank-you.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7550501495850173090'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7550501495850173090'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/software-that-deserves-thank-you.html' title='Software that deserves a thank you'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3511225762544013592</id><published>2009-11-08T23:03:00.000-05:00</published><updated>2009-11-08T23:04:25.876-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='unladen-swallow'/><title type='text'>Another Unladen Swallow Optimization</title><content type='html'>This past week I described a few optimizations that the Unladen Swallow team have done in order to speed up CPython.  In particular one of the optimizations I described was to emit direct calls to C functions that took either zero or one argument.  This improves the speed of Python when calling functions like len() or repr(), who only take one argument.  However, there are plenty of builtin functions that take a fixed number of arguments that is greater than one.  This is the source of the latest optimization.&lt;br /&gt;&lt;br /&gt;As I discussed previously there were two relevant flags, METH_O and METH_NOARGS.  These described functions that take either one or zero arguments.  However, this doesn't cover a wide gamut of functions.  Therefore the first stage of these optimizations was to replace these two flags with METH_FIXED, which indicates that the function takes a fixed number of arguments.  There was also an additional slot added to the struct that holds C functions to store the arity of the function (the number of arguments it takes).  Therefore something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    {"id", builtin_id, METH_O, id_doc}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which is what the struct for a C function looks like would be replaced with:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    {"id", builtin_id, METH_FIXED, id_doc, 1}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This allows Unladen Swallow to emit direct calls to functions that take more than 1 argument, specifically up to 3 arguments.  This results in functions like hasattr() and setattr() to be better optimized.  This change ultimately results in a 7% speed increase in Unladen Swallow's Django benchmark.  Here the speed gains will largely come from avoiding allocating a tuple for the arguments, as Python used to have to do since the functions were defined as METH_VARARGS (which results in it receiving it's arguments as a tuple), as well as avoiding parsing that tuple.&lt;br /&gt;&lt;br /&gt;This change isn't as powerful as it could be, specifically it requires that the function always take the same number of arguments.  This prevents optimizing calls to getattr() for example, which can take either 2 or 3 arguments.  This optimization doesn't hold because C doesn't have any way of expressing default arguments for the function, therefore the CPython runtime must pass all of the needed arguments to a function, which means C functions need to have a way to encode their defaults in a way that CPython can understand.  One of the proposed solutions to this problem is to have functions be able to provide the minimum number of arguments they take and then CPython could pad the provided arguments with NULLs to achieve the correct number of arguments to the function (interestingly the C standard allows more arguments to be passed to a function than it takes).  This type of optimization would speed up calls to things like dict.get() and getattr().&lt;br /&gt;&lt;br /&gt;As you can see the speed of a Python application can be fairly sensitive to how various internal things are handled, in this case the speed increase can be shown to come exclusively from eliminating a tuple allocation and some extra logic on certain function calls.  If you're interested in seeing the &lt;a href="http://code.google.com/p/unladen-swallow/source/detail?r=890"&gt;full changeset it's available on the internet.&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3511225762544013592?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3511225762544013592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/another-unladen-swallow-optimization.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3511225762544013592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3511225762544013592'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/another-unladen-swallow-optimization.html' title='Another Unladen Swallow Optimization'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-975393468580714646</id><published>2009-11-07T22:02:00.001-05:00</published><updated>2009-11-07T22:02:47.905-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='virtualenv'/><category scheme='http://www.blogger.com/atom/ns#' term='pip'/><category scheme='http://www.blogger.com/atom/ns#' term='easy_install'/><category scheme='http://www.blogger.com/atom/ns#' term='virtualenvwrapper'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>My Workflow</title><content type='html'>About a year ago I blogged about how I didn't like easy_install, and I alluded to the fact that I didn't really like any programming language specific package managers.  I'm happy to say I've changed my tune quite drastically in the past 2 months.  Since I started working with Eldarion I've dived head first into the &lt;a href="http://pip.openplans.org/"&gt;pip&lt;/a&gt; and &lt;a href="http://pypi.python.org/pypi/virtualenv"&gt;virtualenv&lt;/a&gt; system and I'm happy to say it works brilliantly.  The nature of the work is that we have lots of different projects all at once, often using wildly different versions of packages in all sorts of incompatible ways.  The only way to stay sane is to have isolated environments for each of them.  Enter virtualenv stage left.&lt;br /&gt;&lt;br /&gt;If you work with multiple Python projects that use different versions of things virtualenv is indispensable.  It allows you to have totally isolated execution environments for different projects.  I'm also using &lt;a href="http://www.doughellmann.com/"&gt;Doug Hellmann's&lt;/a&gt; &lt;a href="http://www.doughellmann.com/projects/virtualenvwrapper/"&gt;virtualenvwrapper&lt;/a&gt;, which wraps up a few virtualenv commands and gives you some hooks you can use.  When I start a new project it looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    $ git checkout some_repo&lt;br /&gt;    $ cd some_repo/&lt;br /&gt;    $ mkvirtualenv project_name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The first two steps are probably self explanatory.  What mkvirtualenv does is to a new virtual environment, and activate it.  I also have a hook set up with virtualenvwrapper to install the latest development version of pip, as well as ipython and ipdb.  pip is a tremendous asset to this process.  It has a requirements file that makes it very easy to keep track of all the dependencies for a given project, plus pip allows you to install packages out of a version control system which is tremendously useful.&lt;br /&gt;&lt;br /&gt;When I want to work on an existing project all I need to do is:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    $ workon project_name&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This activates the environment for that project.  Now the PATH prioritizes stuff installed into that virtualenv, and my Python path only has stuff installed into this virtualenv.  I can't imagine what my job would be like without these tools, if I had to manually manage the dependencies for each project I'd probably go crazy within a week.  Another advantage is it makes it easy to test things against multiple versions of a library.  I can test if something works on Django 1.0 and 1.1 just by switching which environment I'm in.&lt;br /&gt;&lt;br /&gt;As promised tomorrow I'll be writing about an optimization that just landed in Unladen Swallow, and I'm keeping Monday's post a secret.  I'm not sure what Tuesday's post will be, but I think I'll be writing something Django related, either about my new templatetag library, or the state of my multiple database work.  See you around the internet.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-975393468580714646?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/975393468580714646/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/my-workflow.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/975393468580714646'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/975393468580714646'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/my-workflow.html' title='My Workflow'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1955330441532928689</id><published>2009-11-06T23:16:00.002-05:00</published><updated>2009-11-07T19:24:31.147-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='template'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Towards a Better Template Tag Definition Syntax</title><content type='html'>&lt;a href="http://ericholscher.com/"&gt;Eric Holscher&lt;/a&gt; has blogged a &lt;a href="http://ericholscher.com/blog/2009/nov/3/class-based-template-tags/"&gt;few&lt;/a&gt; &lt;a href="http://ericholscher.com/blog/2009/nov/3/making-template-parsing-easier/"&gt;times&lt;/a&gt; this month about various template tag definition syntax ideas.  In particular he's looked at a system based on &lt;a href="http://github.com/codysoyland/surlex"&gt;Surlex&lt;/a&gt; (which is essentially an alternate syntax for certain parts of regular expressions), and a system based on keywords.  I highly recommend giving his posts a read as they explain the ideas he's looked at in far better detail than I could.  However, I wasn't particularly satisfied with either of these solution, I love Django's use of regular expressions for URL resolving, however, for whatever reason I don't really like the look of using regular expressions (or an alternate syntax like Surlex) for template tag parsing.  Instead I've been thinking about an object based parsing syntax, similar to &lt;a href="http://pyparsing.wikispaces.com/"&gt;PyParsing&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;This is an idea I've been thinking about for several months now, but Eric's posts finally gave me the kick in the pants I needed to do the work.  Therefore, I'm pleased to announce that I've released &lt;a href="http://github.com/alex/django-templatetag-sugar"&gt;django-kickass-templatetags&lt;/a&gt;.  Yes, I'm looking for a better name, it's already been pointed out to me that a name like that won't fly in the US government, or most corporate environments.  This library is essentially me putting to code everything I've been thinking about, but enough talking let's take a look at what the template tag definition syntax:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    @tag(register, [Constant("for"), Variable(), Optional([Constant("as"), Name()])]):&lt;br /&gt;    def example_tag(context, val, asvar=None):&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see it's a purely object based syntax, with different classes for different components of a template tag.  For example this would parse something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    {% example_tag for variable %}&lt;br /&gt;    {% example_tag for variable as new_var %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It's probably clear that this is significantly less code than the manual parsing, manual node construction, and manual resolving of variable that you would have needed to do with a raw templatetag definition.  Then the function you have gets the resolved values for each of its parameters, and at that point it's basically the same as Node.render, it is expected to either return a string to be inserted into the template or alter the context.  I'm looking forward to never writing manual template parsing again.  However, there are still a few scenarios it doens't handle, it won't handle something like the logic in the {% if %} tag, and it won't handle tags with {% end %} type tags.  I feel like these should both be solvable problems, but since it's a bolt-on addition to the existing tools it ultimately doesn't have to cover every use case, just the common ones (when's the last time you wrote your own implementation of the {% if %} or {% for %} tags).&lt;br /&gt;&lt;br /&gt;It's my hope that something like this becomes popular, as a) developers will be happier, b) moving towards a community standard is the first step towards including a solution out of the box.  The pain and boilerplate of defining templatetags has long been a complain about Django's template language (especially because the language itself limits your ability to perform any sort of advanced logic), therefore making it as painless as possible ultimately helps make the case for the philosophy of the language itself (which I very much agree with it).&lt;br /&gt;&lt;br /&gt;In keeping with my promise I'm giving an overview of what my next post will be, and this time I'm giving a full 3-day forecast :).  Tommorow I'm going to blog about pip, virtualenv, and my development workflow.  Sunday's post will cover a new optimization that just landed in Unladen Swallow.  And finally Monday's post will contain a strange metaphor, and I'm not saying any more :P.  &lt;a href="http://github.com/alex/django-templatetag-sugar"&gt;Go checkout the code and enjoy.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Edit: Since this article was published the name of the library was changed to be: django-templatetag-sugar.  I've updated all the links in this post.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1955330441532928689?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1955330441532928689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/towards-better-template-tag-definition.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1955330441532928689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1955330441532928689'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/towards-better-template-tag-definition.html' title='Towards a Better Template Tag Definition Syntax'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5486694113588267806</id><published>2009-11-05T23:07:00.001-05:00</published><updated>2009-11-05T23:09:02.976-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='pc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon'/><category scheme='http://www.blogger.com/atom/ns#' term='talk'/><title type='text'>The Pycon Program Committee and my PyCon Talk</title><content type='html'>Last year I presented at a conference for the first time in my life at PyCon, I moderated a panel on ORMs and I enjoyed it a ton (and based on the feedback I've gotten at least a few people enjoyed attending it).  Above and beyond that the two PyCons I've attended have both been amazing conferences, tons of awesome talks, great people to hang out with, and an awesome environment for maximizing both.  For both of the last two years I've hung around the PyCon organizers mailing list since the conference was in Chicago and I lived there, however this year I really wanted to increase my contributions to such a great conference.  Therefore, I joined the Pycon program committee.  This committee is responsible for reviewing all talk submissions and selecting the talks that will ultimately appear at PyCon.&lt;br /&gt;&lt;br /&gt;This year the PyCon programming committee had a really gargantuan task.  There were more talks submitted then ever before, more than 170 of them, for only 90 or so slots.  Unfortunately this meant that we had to reject some really good talks, which always sucks.  There's been a fair bit of discussion about the process this year and what can be done to improve it.  As a reviewer the one thing I wish I'd known going in was that the votes left on talks were just a first round, and ultimately didn't count for a huge amount.  Had I known this I would have been less tepid in giving positive reviews to talks which merely looked interesting.&lt;br /&gt;&lt;br /&gt;Another hot topic in the aftermath is whether or not the speaker's identity should factor into a reviewer's decision.  My position is that it should wherein the speaker has a reputation, be it good or bad.  If I know a speaker is awesome I'm way more likely to give them the +1, likewise if I see a speaker has a history of poor talks I'm more likely to give them a -1.  That being said I don't think new speakers, or slightly inexperienced ones should be penalized for that, I was a brand new speaker last time and I'm grateful I was given the chance to present.&lt;br /&gt;&lt;br /&gt;To give an example of this one of the talks I'm really looking forward to is Mark Ramm's, "To relate or not to relate, that is the question".  Mark and I spoke about this topic for quite a while at PyOhio, and every talk from Mark I've ever seen has been excellent.  Therefore I was more than willing to +1 it.  However, had I not known the speaker it would still have been a good proposal, and an interesting topic, I just would not have gotten immediately excited about going to see the talk.&lt;br /&gt;&lt;br /&gt;As an attendee one of the things I've always found is that speakers who are very passionate about their topics almost always give talks I really enjoy.  Thinking back to my first PyCon Maciej Fijalkowski managed to get me excited and interested in PyPy in just 30 minutes, because he was so passionate in speaking about the topic.&lt;br /&gt;&lt;br /&gt;All that being said I wanted to share a short list of the talks I'm excited about this year, before I dive into what my own talk will be about:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Optimizations And Micro-Optimizations In CPython&lt;/li&gt;&lt;li&gt;Unladen Swallow: fewer coconuts, faster Python&lt;/li&gt;&lt;li&gt;Managing the world's oldest Django project&lt;/li&gt;&lt;li&gt;Understanding the Python GIL&lt;/li&gt;&lt;li&gt;The speed of PyPy&lt;/li&gt;&lt;li&gt;Teaching compilers with python&lt;/li&gt;&lt;li&gt;To relate or not to relate, that is the question&lt;/li&gt;&lt;li&gt;Modern version control: Mercurial internals&lt;/li&gt;&lt;li&gt;Eventlet: Asynchronous I/O with a synchronous interface&lt;/li&gt;&lt;li&gt;Hg and Git : Can't we all just get along?&lt;/li&gt;&lt;li&gt;Mastering Team Play: Four powerful examples of composing Python tools&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;It's a bit of a long list, but compared to the size of the list of accepted talks I'm sure there are quite a few gems I've missed.&lt;br /&gt;&lt;br /&gt;The talk I'm going to be giving this year is about the real time web, also known as HTTP push, Comet, or reverse Ajax.  All of those are basically synonyms for the server being able to push data to the browser, rather than having the browser constantly poll the server for data.  Specifically I'm going to be looking at my experience building three different things, LeafyChat, DjangoDose's DjangoCon stream, and Hurricane.&lt;br /&gt;&lt;br /&gt;Leafychat is an IRC client built for the DjangoDash by myself, Leah Culver, and Chris Wanstrath.  The DjangoDose DjangoCon stream was a live stream of all the Twitter items about DjangoCon that Eric Florenzano and I built in the week leading up to DjangoCon.  Finally, Hurricane is the library Eric Florenzano and I have been working on in order to abstract the lessons learned from our experience building "real time" applications in Python.&lt;br /&gt;&lt;br /&gt;In the talk I'm going to try to zero in on what we did for each of these projects, what worked, what didn't, and what I'm taking away from the experience.  Finally, Eric Florenzano and I are working to put together a new updated, better version of the DjangoCon stream for PyCon.  I'm going to discuss what we do with that project, and why we do it that way in light of the lessons of previous projects.&lt;br /&gt;&lt;br /&gt;I'm hoping both my talk, and all of them will be awesome.  One thing's for sure, I'm already looking forward to PyCon 2010.  Tomorrow I'm going to be writing about my thoughts on a more ideal template tag definition syntax for Django, and hopefully sharing some code if I have time to start working on it.  See you then (and in Atlanta ;))!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5486694113588267806?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5486694113588267806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/pycon-program-committee-and-my-pycon.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5486694113588267806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5486694113588267806'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/pycon-program-committee-and-my-pycon.html' title='The Pycon Program Committee and my PyCon Talk'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1084208212608576604</id><published>2009-11-04T21:32:00.001-05:00</published><updated>2009-11-04T21:33:47.706-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='gsoc'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Django's ManyToMany Refactoring</title><content type='html'>If you follow Django's development, or caught next week's DjangoDose Tracking Trunk episode (what?  that's not how time flows you say?  too bad) you've seen the recent ManyToManyField refactoring that Russell Keith-Magee committed.  This refactoring was one of the results of my work as a Google Summer of Code student this summer.  The aim of that work was to bring multiple database support to Django's ORM, however, along the way I ran into refactor the way ManyToManyField's were handled, the exact changes I made are the subject of tonight's post.&lt;br /&gt;&lt;br /&gt;If you've looked at django.db.models.fields.related you may have come away asking how code that messy could possibly underlie Django's amazing API for handling related objects, indeed the mess so is so bad that there's a comment which says:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    # HACK&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;which applies to an entire class.  However, one of the real travesties of this module was that it contained a large swath of raw SQL in the manager for ManyToMany relations, for example the clear() method's implementation looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    cursor = connection.cursor()&lt;br /&gt;    cursor.execute("DELETE FROM %s WHERE %s = %%s" % \&lt;br /&gt;        (self.join_table, source_col_name),&lt;br /&gt;        [self._pk_val])&lt;br /&gt;    transaction.commit_unless_managed()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see this hits the trifecta, raw SQL, manual transaction handling, and the use of a global connection object.  From my perspective the last of these was the biggest issue.  One of the tasks in my multiple database branch was to remove all uses of the global connection object, and since this uses it it was a major target for refactoring.  However, I really didn't want to rewrite any of the connection logic I'd already implemented in QuerySets.  This desire to avoid any new code duplication, coupled with a desire to remove the existing duplication (and flat out ugliness), led me to the simple solution: use the existing machinery.&lt;br /&gt;&lt;br /&gt;Since Django 1.0 developers have been able to use a full on model for the intermediary table of a ManyToMany relation, thanks to the work of Eric Florenzano and Russell Keith-Magee.  However, that support was only used when the user explicitly provided a through model.  This of course leads to a lot of methods that basically have two implementation: one for the through model provided case, and one for the normal case -- which is yet another case of code bloat that I was now looking to eliminate.  After reviewing these items my conclusion was that the best course was to use the provided intermediary model if it was there, otherwise create a full fledged model with the same fields (and everything else) as the table that would normally be specially created for the ManyToManyField.&lt;br /&gt;&lt;br /&gt;The end result was dynamic class generation for the intermediary model, and simple QuerySet methods for the methods on the Manager, for example the clear() method I showed earlier now looks like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    self.through._default_manager.filter(**{&lt;br /&gt;        source_field_name: self._pk_val&lt;br /&gt;    }).delete()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Short, simple, and totally readable to anyone with familiarity with Python and Django.  In addition this move allowed Russell to fix another ticket with just two lines of code.  All in all this switch made for cleaner, smaller code and fewer bugs.&lt;br /&gt;&lt;br /&gt;Tomorrow I'm going to be writing about both the talk I'm going to be giving at PyCon, as well as my experience as a member of the PyCon program committee.  See you then.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1084208212608576604?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1084208212608576604/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/djangos-manytomany-refactoring.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1084208212608576604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1084208212608576604'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/djangos-manytomany-refactoring.html' title='Django&apos;s ManyToMany Refactoring'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2691570477049490729</id><published>2009-11-03T19:58:00.001-05:00</published><updated>2009-11-03T20:12:00.004-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><category scheme='http://www.blogger.com/atom/ns#' term='unladen-swallow'/><title type='text'>Diving into Unladen Swallow's Optimizations</title><content type='html'>Yesterday I described the general architecture of Unladen Swallow, and I said that just by switching to a JIT compiler and removing the interpretation overhead Unladen Swallow was able to get a performance gain.  However, that gain is nowhere near what the engineers at Google are hoping to accomplish, and as such they've been working on building various optimizations into their JIT.  Here I'm going to describe two particularly interesting ones they implemented during the 3rd quarter (they're doing quarterly releases).&lt;br /&gt;&lt;br /&gt;Before diving into the optimizations themselves I should note there's one piece of the Unladen Swallow architecture I didn't discuss in yesterday's post.  The nature of dynamic languages is that given code can do nearly anything depending on the types of the variables present, however in practice usually very few types are seen.  Therefore it is necessary to collect information about the types seen in practice in order to perform optimizations.  Therefore what Unladen Swallow has done is added data collection to the interpreter while it is executing bytecode.  For example the BINARY_ADD opcode records the types of both of it's operands, the CALL_FUNCTION opcode records the function it is calling, and the UNPACK_SEQUENCE opcode records the type of the sequence it's unpacking.  This data is then used when the function is compiled to generate optimal code for the most likely scenarios.&lt;br /&gt;&lt;br /&gt;The first optimization I'm going to look at is one for the CALL_FUNCTION opcode.  Python has a number of flags that functions defined in C can have, the two relevant to this optimization are METH_NOARGS and METHO_O.  These flags indicate that the function (or method) in question take either 0 or 1 argument respectively (this is excluding the self argument on methods).  Normally when Python calls a function it builds up a tuple of the arguments, and a dictionary for keyword arguments.  For functions defined in Python CPython lines up the arguments with those the function takes and then sets them as local variables for the new function.  For C functions they are given the tuple and dictionary directly and are responsible for parsing them themselves.  By contrast functions with METH_NOARGS or METH_O receive their arguments (or nothing in the case of METH_NOARGS) directly.&lt;br /&gt;&lt;br /&gt;Because calling METH_NOARGS and METH_O functions is so much easier than the alternate case (which involves several allocations and complex logic) when possible it is best to special case them in the generated assembly.  Therefore, when compiling a CALL_FUNCTION opcode if using the data recorded there is only ever one function called (imagine a call to len, it is going to be the same len function every time), and that function is METH_NOARGS or METH_O then instead of generating a call to the usual function call machinery Unladen Swallow instead emits a check to make sure the function is actually the expected one and if it passes emits a call directly to the function with the correct arguments.  If this guard fails then Unladen Swallow jumps back to the regular interpreter, leaving the optimized assembly.  The reason for this is that the ultimately generated assembly can be more efficient when it only has to consider one best case scenario, as opposed to needing to deal with a large series of if else statements, which catalogue every single best case and the corresponding normal case.  Ultimately, this results in more efficient code for calls to functions like len(), which are basically never redefined.&lt;br /&gt;&lt;br /&gt;The next optimization we're going to look at is one for the LOAD_GLOBAL function.  The LOAD_GLOBAL  opcode is used for getting the value of a global variable, such as a builtin function, an imported class, or a global variable in the same module.  In the interpreter the code for this opcode looks something like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    name = OPARG()&lt;br /&gt;    try:&lt;br /&gt;        value = globals[name]&lt;br /&gt;    except KeyError:&lt;br /&gt;        try:&lt;br /&gt;            value = builtins[name]&lt;br /&gt;        except KeyError:&lt;br /&gt;            raise_exception(KeyError, name)&lt;br /&gt;    PUSH(value)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;As you can see in the case of a builtin object (something like len, str, or dict) there are two dictionary lookups.  While the Python dictionary is an exceptionally optimized datastructure it still isn't fast compared to a lookup of a local value (which is a single lookup in a C array).  Therefore the goal of this optimization is to reduce the number of dictionary lookups needed to find the value for a global or builtin.&lt;br /&gt;&lt;br /&gt;The way this was done was for code objects (the datastructures that hold the opcodes and various other internal details for functions) to register themselves with the globals and builtin dictionaries.  By registering themselves the dictionaries are able to notify the code objects (similar to Django signals) whenever they are modified.  The result of this is that the generated assembly for a LOAD_GLOBAL can perform the dictionary lookup once at compilation time and then the resulting assembly will be valid until the globals or builtins dictionary notifies the code object that they have been modified, thus rendering the assembly invalid.  In practice this is very efficient because globals and builtins are very rarely modified.&lt;br /&gt;&lt;br /&gt;Hopefully you've gotten a sense of the type of work that the people behind Unladen Swallow are doing.  If you're interested in reading more on this type of work I'd highly recommend taking a look at the literature listed on the &lt;a href="http://code.google.com/p/unladen-swallow/wiki/RelevantPapers"&gt;Unladen Swallow wiki&lt;/a&gt;, as they note that there is no attempt to do any original research, all the work being done is simply the application of existing, proven techniques to the CPython interpreter.&lt;br /&gt;&lt;br /&gt;For the rest of this month I'm going to try to give a preview of the next day's post with each post, that way I can start thinking about it well in advance.  Tomorrow I'm going to shift gears a little bit and write about the ManyToManyField refactoring I did over the summer and which was just committed to Django.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2691570477049490729?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2691570477049490729/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/diving-into-unladen-swallows.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2691570477049490729'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2691570477049490729'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/diving-into-unladen-swallows.html' title='Diving into Unladen Swallow&apos;s Optimizations'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2178119911644916673</id><published>2009-11-02T22:29:00.003-05:00</published><updated>2009-11-03T15:04:11.048-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><category scheme='http://www.blogger.com/atom/ns#' term='unladen-swallow'/><title type='text'>Introduction to Unladen Swallow</title><content type='html'>Unless you've been living under a rock for the past year (or have zero interest in either Python or dynamic languages, it which case why are you here?) you've probably heard of Unladen Swallow.  Unladen Swallow is a Google funded branch of the CPython interpreter, with a goal of making CPython significantly faster while retaining both API and ABI compatibility.  In this post I'm going to try to explain what it is Unladen Swallow is doing to bring a new burst of speed to the Python world.&lt;br /&gt;&lt;br /&gt;In terms of virtual machines there are a few levels of complexity, which roughly correspond to their speed.  The simplest type of interpreter is an AST evaluator, these are more or less the lowest of the low on the speed totem pole, up until YARV was merged into the main Ruby interpreter, MRI (Matz Ruby Interpreter) was this type of virtual machine.  The next level of VM is a bytecode interpreter, this means that the language is compiled to an intermediary format (bytecode) which is then executed.  Strictly speaking this is an exceptionally broad category which encompasses most virtual machines today, however for the purposes of this article I'm going to exclude any VMs with a Just-In-Time compiler from this section (more on them later).  The current CPython VM is this type of interpreter.  The most complex (and fastest) type of virtual machine is one with a Just-In-Time compiler, this means that the bytecode that the virtual machine interprets is also dynamically compiled into assembly and executed.  This type of VM includes modern Javascript interpreters such as V8, Tracemonkey, and Squirellfish, as well as other VMs like the Hotspot Java virtual machine.&lt;br /&gt;&lt;br /&gt;Now that we know where CPython is, and what the top of the totem pole looks like it's probably clear what Unladen Swallow is looking to accomplish, however there is a bit of prior art here that's worthy of taking a look.  There is actually currently a JIT for CPython, named Psyco.  Psyco is pretty commonly used to speed up numerical code, as that's what it's best at, but it can speed up most of the Python language.  However, Psyco is extremely difficult to maintain and update.  It only recently gained support for modern Python language features like generators, and it still only supports x86 CPUs.  For these reasons the developers at Google chose to build their JIT rather than work to improve the existing solution (they also chose not to use one of the alternative Python VMs, I'll be discussing these in another post).&lt;br /&gt;&lt;br /&gt;I just said that Unladen Swallow looked to build their own JIT, but that's not entirely true.  The developers have chosen not to develop their own JIT (meaning their own assembly generator, and register allocator, and optimizer, and everything else that goes along with a JIT), they have instead chosen to utilize the LLVM (Low Level Virtual Machine) JIT for all the code generation.  What this means is that instead of doing all the work I've alluded the devs can instead translate the CPython bytecode into LLVM IR (intermediate representation) and then use LLVM's existing JIT infrastructure to do some of the heavy lifting.  This gives the devs more time to focus on the interesting work of how to optimize the Python language.&lt;br /&gt;&lt;br /&gt;Now that I've layed out the background I'm going to dive into what exactly it is that Unladen Swallow does.  Right now the CPython virtual machine looks something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    for opcode in opcodes:&lt;br /&gt;        if opcode == BINARY_ADD:&lt;br /&gt;            x, y = POP(), POP()&lt;br /&gt;            z = x + y&lt;br /&gt;            PUSH(z)&lt;br /&gt;        elif opcode == JUMP_ABSOLUTE:&lt;br /&gt;            pc = OPARG()&lt;br /&gt;        # ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is both hugely simplified and translated into a Pythonesque psuedocode, but hopefully it makes the point clear, right now the CPython VM runs through the opcodes and based on what the opcode is executes some C code.  This is particularly inefficient because there is a fairly substantial overhead to actually doing the dispatch on the opcode.  What Unladen Swallow does is count the number of times a given Python function is called (the heuristic is actually slightly more complicated than this, but it's a good approximation of what happens), and when it reaches 10000 (the same value the JVM uses) it stops to compile the function using LLVM.  Here what it does is essentially unrolls the interpreter loop, into the LLVM IR.  So if you had the bytecode:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    BINARY_ADD&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Unladen Swallow would generate code like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    x, y = POP(), POP()&lt;br /&gt;    z = x + y&lt;br /&gt;    PUSH(z)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This eliminates all of the overhead of the large loop in the interpreter.  Unladen Swallow also performs a number of optimizations based on Python's semantics, but I'll be getting into those in another post, for now LLVM run it's optimizers, which can improve the generated code somewhat, and then CPython executes the generated function.  Now whenever this function is called in the future the optimized, assembly version of it is called.&lt;br /&gt;&lt;br /&gt;This concludes the introduction to Unladen Swallow.  Hopefully you've learned something about the CPython VM, Unladen Swallow, or virtual machines in general.  In future posts I'm going to be diving in to some of the optimizations Unladen Swallow does, as well as what other players are doing in this space (particularly PyPy).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2178119911644916673?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2178119911644916673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/introduction-to-unladen-swallow.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2178119911644916673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2178119911644916673'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/introduction-to-unladen-swallow.html' title='Introduction to Unladen Swallow'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6112873967947073661</id><published>2009-11-01T23:30:00.002-05:00</published><updated>2009-11-01T23:33:34.263-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>Another month of blogging?</title><content type='html'>Last year I started this blog during the November, blog every day for a month month.  This year I'm hoping to repeat the feat, blogging every single day this month.  Today's post is a bit light on content, but I'm hoping to give a preview of what I'm going to be blogging about.  This month I'm hopping to blog about advanced Django techniques, Comet (and other HTTP push technology), and I'm probably going to be adding some more political content to the usual technical content of this blog.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Lastly, I'm hoping to move this blog either to another hosted provider, or something on my own servers, but somewhere where I can get a tad bit more control.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;In any event here's to a productive month of bloggging!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6112873967947073661?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6112873967947073661/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/11/another-month-of-blogging.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6112873967947073661'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6112873967947073661'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/11/another-month-of-blogging.html' title='Another month of blogging?'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1684818269255100971</id><published>2009-10-10T22:03:00.002-04:00</published><updated>2009-10-10T22:08:31.206-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='compiler'/><category scheme='http://www.blogger.com/atom/ns#' term='pypy'/><category scheme='http://www.blogger.com/atom/ns#' term='unladen-swallow'/><title type='text'>Optimising compilers are there so that you can be a better programmer</title><content type='html'>In a discussion on the Django developers mailing list I recently commented that the performance impact of having logging infrastructure, in the case where the user doesn't want the logging, could essentially be disregarded because Unladen Swallow (and PyPy) are bringing us a proper optimising (Just in Time) compiler that would essentially remove that consideration.  Shortly thereafter someone asked me if I really thought it was the job of the interpreter/compiler to make us not think about performance.  And my answer is: the job of a compiler is to let me program with best practices and not suffer performance consequences for doing things the right way.&lt;br /&gt;&lt;br /&gt;Let us consider the most common compiler optimisations.  A relatively simple one is function inlining, in the case where including the body of the function would be more efficient than actually calling it, a compiler can simply move the functions body into its caller.  However, we can actually do this optimisation in our own code.  We could rewrite:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def times_2(x):&lt;br /&gt;        return x * 2&lt;br /&gt;    &lt;br /&gt;    def do_some_stuff(i):&lt;br /&gt;        for x in i:&lt;br /&gt;            # stuff&lt;br /&gt;            z = times_2(x)&lt;br /&gt;        # more stuff&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;as:&lt;br /&gt;    &lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def do_some_stuff(i):&lt;br /&gt;        for x in i:&lt;br /&gt;            # stuff&lt;br /&gt;            z = x * 2&lt;br /&gt;        # more stuff&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And this is a trivial change to make.  However in the case where ``times_2`` is slightly less trivial, and is used a lot in our codebase it would be exceptionally more programming practice to repeat this logic all over the place, what if we needed to change it down the road?  Then we'd have to review our entire codebase to make sure we changed it everywhere.  Needless to say that would suck.  However, we don't want to give up the performance gain from inlining this function either.  So here it's the job of the compiler to make sure functions are inlined when possible, that way we get the best possible performance, as well as allowing us to maintain our clean codebase.&lt;br /&gt;&lt;br /&gt;Another common compiler optimisation is to transform multiplications by powers of 2 into binary shifts.  Thus ``x * 2`` becomes ``x &lt;&lt; 1``.  However, humans don't usually think in terms of binary operations, and it makes our codebase less readable therefore.  Once again, however, it does give better performance.  This is another case where best practices are in conflict with efficient code.  However, once again, our compiler is capable of solving this for us.  When ``x`` is known to be an integer it is trivial for a compiler to make this transformation for us, once again allowing us to have both the performance we want, and the good programming practices we need.&lt;br /&gt;&lt;br /&gt;A final optimisation we will consider is constant propagation.  Many program have constants that are used throughout the codebase.  These are often simple global variables.  However, once again, inlining them into methods that use them could provide a significant benefit, by not requiring the code to making a lookup in the global scope whenever they are used.  But we really don't want to do that by hand, as it makes our code less readable ("Why are we multiplying this value by  this random float?", "You mean pi?", "Oh."), and makes it more difficult to update down the road.  Once again our compiler is capable of saving the day, when it can detect a value is a constant it can propagate it throughout the code.&lt;br /&gt;&lt;br /&gt;So does all of this mean we should never have to think about writing optimal code, the compiler can solve all problems for us?  The answer to this is a resounding no.  A compiler isn't going to rewrite your insertion sort into Tim sort, nor is it going to fix the fact that you do 700 SQL queries to render your homepage.  What the compiler can do is allow you to maintain good programming practices.&lt;br /&gt;&lt;br /&gt;So what does this mean for logging in Django?  Fundamentally it means that we shouldn't be concerned with possible overhead from calls that do nothing (in the case where we don't care about the logging) since a good compiler will be able to eliminate those for us.  In the case where we actually do want to do something (say write the log to a file) the overhead is unavoidable, we have to open a file and write to it, there's no way to optimise it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1684818269255100971?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1684818269255100971/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/10/optimising-compilers-are-there-so-that.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1684818269255100971'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1684818269255100971'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/10/optimising-compilers-are-there-so-that.html' title='Optimising compilers are there so that you can be a better programmer'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1317682186585927801</id><published>2009-08-14T15:42:00.001-04:00</published><updated>2009-08-14T15:43:56.132-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='filter'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Django-filter 0.5 released!</title><content type='html'>I've just tagged and uploaded Django-filter 0.5 to PyPi.  The biggest change this release brings is that the package name has been changed from `filter` to `django_filters` in order to avoid conflicts with the Python builtin `filter`.  Other changes included the addition of an `initial` argument on Filters, as well as the addition of an `AllValuesFilter` which is a `ChoiceFilter` who's choices are any values currently in the DB for that field.  Despite the change in package name I will not be changing the name of the project due to the overhead in moving the repository (Github doesn't set up redirects when you change a project's name) and the PyPi package.  I hope everyone enjoys this new release, as a lot of it's improvements have come out of my usage on Django-filter in piano-man.&lt;br /&gt;&lt;br /&gt;As for what the future holds several people have indicated their interest in the inclusion of django-filter in Django itself as a contrib package, and for usage in the Admin as a new implementation of the `list_filter` option that is more flexible.  Because of this my next work is probably going to be on implementing a custom `ModelAdmin` class that uses `FilterSets` for filtering.&lt;br /&gt;&lt;br /&gt;You can find the latest release on &lt;a href="http://pypi.python.org/pypi/django-filter"&gt;PyPi&lt;/a&gt; and &lt;a href="http://github.com/alex/django-filter/tree/master"&gt;Github&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1317682186585927801?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1317682186585927801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/08/django-filter-05-released.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1317682186585927801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1317682186585927801'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/08/django-filter-05-released.html' title='Django-filter 0.5 released!'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1935043119691547461</id><published>2009-07-12T11:30:00.002-04:00</published><updated>2009-07-12T12:01:13.981-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='vcs'/><title type='text'>pyvcs .2 released</title><content type='html'>Hot on the heels of our .1 release (it's only been a week!) I'm pleased to announce the .2 release of pyvcs.  This release brings with it lots of new goodies.  Most prominent among these are the newly-added Subversion and Bazaar backends.  There are also several bug fixes to the code browsing features of the Mercurial backend.  This release can be found at:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;PyPi: &lt;a href="http://pypi.python.org/pypi/pyvcs"&gt;http://pypi.python.org/pypi/pyvcs&lt;/a&gt;&lt;/li&gt;&lt;li&gt;Github: &lt;a href="http://github.com/alex/pyvcs/"&gt;http://github.com/alex/pyvcs/&lt;/a&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;span style="display: block;" id="formatbar_Buttons"&gt;&lt;span class=" on down" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;If you find any bugs with this release please report them at:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://github.com/alex/pyvcs/issues"&gt;http://github.com/alex/pyvcs/issues&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Thanks for all the contributions to this release, almost everything you see in this release is because community members contributed to it, hardly any new code in here is originally written by &lt;a href="http://www.justinlilly.com/"&gt;Justin&lt;/a&gt; or I.&lt;br /&gt;&lt;br /&gt;We are hoping to have some exciting announcements for &lt;a href="http://github.com/alex/piano-man/"&gt;piano-man&lt;/a&gt; coming up in the next couple of weeks.&lt;br /&gt;&lt;br /&gt;Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1935043119691547461?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1935043119691547461/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/07/pyvcs-2-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1935043119691547461'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1935043119691547461'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/07/pyvcs-2-released.html' title='pyvcs .2 released'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6932980174327128400</id><published>2009-07-05T13:10:00.006-04:00</published><updated>2009-07-09T21:05:16.772-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='vcs'/><title type='text'>Announcing pyvcs, django-vcs, and piano-man</title><content type='html'>&lt;a href="http://www.justinlilly.com/"&gt;Justin Lilly&lt;/a&gt; and I have just released pyvcs, a lightweight abstraction layer on top of multiple version control systems, and django-vcs, a Django application leveraging pyvcs in order to provide a web interface to version control systems.  At this point pyvcs exists exclusively to serve the needs of django-vcs, although it is separate and completely usable on its own.  Django-vcs has a robust feature set, including listing recent commits, pretty diff rendering of commits, and code browsing.  It also supports multiple projects.  Both pyvcs and django-vcs currently support Git and Mercurial, although adding support for a new backend is as simple as implementing four methods and we'd love to be able to support additional VCS like Subversion or Bazaar.  Django-vcs comes with some starter templates (as well as CSS to support the pretty diff rendering).&lt;br /&gt;&lt;br /&gt;It goes without saying that we'd like to thank the authors of the VCS themselves, in addition we'd like to thank the authors of Dulwich, for providing a pure Python implementation of the Git protocol, as well as the Pocoo guys, for pygments, the syntax highlighting library for Python, as well as the pretty diff rendering which we lifted out of the lodgeit pastbin application.&lt;br /&gt;&lt;br /&gt;Having announced what we have already, we'll now be looking towards the future.  As such Justin and I plan to be starting a new Django project "piano-man".  Piano-man, a reference to Billy Joel, follows the Django tradition of naming things after musicians (although we've branched out a bit, leaving the realm of Jazz in favour of Rock 'n Roll).  Piano-man will be a complete web based project management system, similar to Trac or Redmine.  There are a number of logistical details that we still need to sort out, such as whether this will be a part of Pinax as a "code edition" or whether it will be a separate project entirely, like Satchmo and Reviewboard.&lt;br /&gt;&lt;br /&gt;Some people are inevitably asking why we've chosen to start a new project, instead of working to improve one of the existing ones I alluded to.  The reason is, after hearing coworkers complain about poor Git support in Trac (even with external plugins), and friends complain about the need to modify Redmine just to support branches in Git properly I'd become convinced it couldn't possibly be that hard, and I think Justin and I have proven that it isn't.  All the work you see in pyvcs and django-vcs took 48 hours to complete, with both of us working evenings and a little bit during the day on these projects.&lt;br /&gt;&lt;br /&gt;You can find both django-vcs and pyvcs on PyPi as well on Github under my account (http://github.com/alex/), both are released under the BSD license.  We hope you enjoy both these projects and find them helpful, and we'd appreciate and contributions, just file bugs on Github.  I'll have another blog post in a few days outlining the plan for piano-man once Justin and I work out the logistics.  Enjoy.&lt;br /&gt;&lt;br /&gt;Edit:  I seem to have forgotten all the relevant links, here they are&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/alex/pyvcs"&gt;pyvcs on github&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/alex/django-vcs/"&gt;django-vcs on github&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://github.com/alex/piano-man"&gt;piano-man on github&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://pypi.python.org/pypi/pyvcs"&gt;pyvcs on PyPi&lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href="http://pypi.python.org/pypi/django-vcs"&gt;django-vcs on PyPi&lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;Sorry about that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6932980174327128400?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6932980174327128400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/07/announcing-pyvcs-django-vcs-and-piano.html#comment-form' title='17 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6932980174327128400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6932980174327128400'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/07/announcing-pyvcs-django-vcs-and-piano.html' title='Announcing pyvcs, django-vcs, and piano-man'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>17</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6171883798243766809</id><published>2009-06-04T23:27:00.002-04:00</published><updated>2009-06-04T23:58:59.321-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='response'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>A response to "Python sucks"</title><content type='html'>I recently saw an article on Programming Reddit, titled, "Python sucks: Why Python is not my favourite programming language".  I personally like Python quite a lot (see my blog title), but I figured I might read an interesting critique, probably from a very different point of view from mine.  Unfortunately that is the opposite of what I found.  The post was, at best, a horribly misinformed inaccurate critique, and at worst an intentionally dishonest, misleading, farce.  The post can be found &lt;a href="http://call-cc.blogspot.com/2009/06/2-rarely-cited-reasons-why-python-sucks.html"&gt;here&lt;/a&gt;.  I felt the need to respond to it precisely because it is so lacking in facts, and reading it one can get impressions that are completely incorrect, and I am hoping I can correct some of these.&lt;br /&gt;&lt;br /&gt;The post's initial statements about iterating over a file are accurate.  However, he then goes on to say Python supports closures (which is true), and follows this with a piece of code that has absolutely nothing to do with closures, it is actually a callable object (or as C++ calls them, functors).  The authors seems to take issue with these (though he doesn't explain why), ignoring the fact that Python has complete support for actual closures, not just callable objects.&lt;br /&gt;&lt;br /&gt;The author then claims that Python has many other such arbitrary rules, using as an example the "yield" keyword.  The author appears to be claiming the behavior of the yield keyword is arbitrary and poorly defined, however it's very unclear what his point actually is, or what the source of his complaints is.  My only response can be to say that the "yield" keyword *always* turns the function it's used in into a generator, that is to say it returns an iterable that lazy evaluates the function, pausing each time it reaches the yield statement, and returning that object.&lt;br /&gt;&lt;br /&gt;The author claims that many of the arbitrary decisions in Python are a result of Guido's insistence on a specific programming style, using as an example crippled lambdas.  It is generally accepted that in Python lambda is just syntactic sugar for defining a function within any context (which the author completely ignores in his discussion of closure).  To say that lambdas are crippled is to ignore the fact that absolutely nothing is rendered impossible by this, except for unreadable one liners.&lt;br /&gt;&lt;br /&gt;The author's final complaint is directed at Python's C-API.  This is possibly his least accurate critique.  The author compares what is necessary to use a C library from within various programming languages.  He shows that in Python all you have to do is import the library like you would for normal Python code.  However, he goes on to say that for this to work you need to write lots of C boilerplate, and says that in other programming languages (showing examples from Haskell and PLT Scheme) this boiler plate is unnecessary.  However, this is a completely disingenuous comparison.  This is because what he is showing for Haskell and Scheme is their foreign function interface, not any actual language level integration.  To do what he shows in Python is perfectly possible using the included ctypes library.  I'm not familiar with the C-API of either Haskell or PLT Scheme, however I imagine that in order to work seamlessly and have the APIs appear the same as in code in those languages it is still necessary to write boiler plate so that the interpreter can recognize them.&lt;br /&gt;&lt;br /&gt;In conclusion that blog post was a critique completely devoid of value, not worth the bytes that are used to store it.  This is not to say there aren't any valid criticisms of Python, there are many, as evidenced by any number of recent blog posts discussing "5 things they hate about technology X", where technology X is something the author likes, because no technology is perfect, however no such honest critique was present here.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6171883798243766809?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6171883798243766809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/06/response-to-python-sucks.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6171883798243766809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6171883798243766809'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/06/response-to-python-sucks.html' title='A response to &quot;Python sucks&quot;'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6558319773424759218</id><published>2009-05-05T16:03:00.004-04:00</published><updated>2009-06-19T16:49:40.280-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='forms'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='talk'/><title type='text'>EuroDjangoCon 2009</title><content type='html'>EuroDjangoCon 2009 is still going strong, but I wanted to share the materials from my talk as quickly as possible.  My slides are on Slide Share:&lt;br /&gt;&lt;br /&gt;&lt;div style="width: 425px; text-align: left;" id="__ss_1390189"&gt;&lt;a style="margin: 12px 0pt 3px; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; display: block; text-decoration: underline;" href="http://www.slideshare.net/kingkilr/forms-getting-your-moneys-worth?type=powerpoint" title="Forms, Getting Your Money's Worth"&gt;Forms, Getting Your Money's Worth&lt;/a&gt;&lt;object style="margin: 0px;" height="355" width="425"&gt;&lt;param name="movie" value="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=formseurodjangocon-090505150331-phpapp01&amp;amp;stripped_title=forms-getting-your-moneys-worth"&gt;&lt;param name="allowFullScreen" value="true"&gt;&lt;param name="allowScriptAccess" value="always"&gt;&lt;embed src="http://static.slidesharecdn.com/swf/ssplayer2.swf?doc=formseurodjangocon-090505150331-phpapp01&amp;amp;stripped_title=forms-getting-your-moneys-worth" type="application/x-shockwave-flash" allowscriptaccess="always" allowfullscreen="true" height="355" width="425"&gt;&lt;/embed&gt;&lt;/object&gt;&lt;div style="font-size: 11px; font-family: tahoma,arial; height: 26px; padding-top: 2px;"&gt;View more &lt;a style="text-decoration: underline;" href="http://www.slideshare.net/"&gt;presentations&lt;/a&gt; from &lt;a style="text-decoration: underline;" href="http://www.slideshare.net/kingkilr"&gt;kingkilr&lt;/a&gt;.&lt;/div&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And the first examples code follows:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.forms.util import ErrorList&lt;br /&gt;from django.utils.datastructures import SortedDict&lt;br /&gt;&lt;br /&gt;def multiple_form_factory(form_classes, form_order=None):&lt;br /&gt;   if form_order:&lt;br /&gt;       form_classes = SortedDict([(prefix, form_classes[prefix]) for prefix in&lt;br /&gt;           form_order])&lt;br /&gt;   else:&lt;br /&gt;       form_classes = SortedDict(form_classes)&lt;br /&gt;   return type('MultipleForm', (MultipleFormBase,), {'form_classes': form_classes})&lt;br /&gt;&lt;br /&gt;class MultipleFormBase(object):&lt;br /&gt;   def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,&lt;br /&gt;       initial=None, error_class=ErrorList, label_suffix=':',&lt;br /&gt;       empty_permitted=False):&lt;br /&gt;       if prefix is None:&lt;br /&gt;           prefix = ''&lt;br /&gt;       self.forms = [form_class(data, files, auto_id, prefix + form_prefix,&lt;br /&gt;           initial[i], error_class, label_suffix, empty_permitted) for&lt;br /&gt;           i, (form_prefix, form_class) in enumerate(self.form_classes.iteritems())]&lt;br /&gt;&lt;br /&gt;   def __unicode__(self):&lt;br /&gt;       return self.as_table()&lt;br /&gt;&lt;br /&gt;   def __iter__(self):&lt;br /&gt;       for form in self.forms:&lt;br /&gt;           for field in form:&lt;br /&gt;               yield field&lt;br /&gt;&lt;br /&gt;   def is_valid(self):&lt;br /&gt;       return all(form.is_valid() for form in self.forms)&lt;br /&gt;&lt;br /&gt;   def as_table(self):&lt;br /&gt;       return '\n'.join(form.as_table() for form in self.forms)&lt;br /&gt;&lt;br /&gt;   def as_ul(self):&lt;br /&gt;       return '\n'.join(form.as_ul() for form in self.forms)&lt;br /&gt;&lt;br /&gt;   def as_p(self):&lt;br /&gt;       return '\n'.join(form.as_p() for form in self.forms)&lt;br /&gt;&lt;br /&gt;   def is_multipart(self):&lt;br /&gt;       return any(form.is_multipart() for form in self.forms)&lt;br /&gt;&lt;br /&gt;   def save(self, commit=True):&lt;br /&gt;       return tuple(form.save(commit) for form in self.forms)&lt;br /&gt;   save.alters_data = True&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;EuroDjangoCon has been a blast thus far and after the conference I'll do a blogpost that does it justice.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6558319773424759218?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6558319773424759218/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/05/eurodjangocon-2009.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6558319773424759218'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6558319773424759218'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/05/eurodjangocon-2009.html' title='EuroDjangoCon 2009'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6003576372070406899</id><published>2009-04-16T18:54:00.003-04:00</published><updated>2009-04-16T20:30:30.588-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='filter'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax_validation'/><title type='text'>Ajax Validation Aministrivia</title><content type='html'>Small administrative note, now that Github has it's own issue tracker users wishing to report issues with django-ajax-validation can now do so on Github, instead of the old Google Code page.  Django-filter users can also report issues on its Github page, in place of the former system of "message or email me", which, though functional, wasn't very convenient.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6003576372070406899?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6003576372070406899/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/04/ajax-validation-aministrivia.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6003576372070406899'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6003576372070406899'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/04/ajax-validation-aministrivia.html' title='Ajax Validation Aministrivia'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2086214551143892565</id><published>2009-03-30T15:30:00.002-04:00</published><updated>2009-03-30T17:31:48.976-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql object'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web2py'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon'/><category scheme='http://www.blogger.com/atom/ns#' term='sql alchemy'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>ORM Panel Recap</title><content type='html'>Having now completed what I thought was a quite successful panel I thought it would be nice to do a review of some of the decisions I made, that some people had been asking about.  For those who missed it you can find a live blog of the event by James Bennett at &lt;a href="http://www.b-list.org/weblog/2009/mar/28/pycon-orm-panel/"&gt;his blog&lt;/a&gt;, and a video should hopefully be going up sometime soon.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Why Google App Engine&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;As Guido pointed out App Engine does not have an ORM, as App Engine doesn't have a relational datastore.  However, it does have something that looks and acts quite a lot like other ORMs, and it does fundamentally try to serve the same purpose, offering a persistence layer.  Therefore I decided it was at least in the same class of items I wanted to add.  Further, with the rise of non-relational DBs that all fundamentally deal with the same issues as App Engine, and the relationship between ORMs and these new persistence layers I thought it would be advantageous to have one of these, Guido is a knowledgeable and interesting person, and that's how the cookie crumbled.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Why Not ZODB/Storm/A Talking Pony&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Time.  I would have loved to have as many different ORMs/things like them as exist in the Python eco-sphere, but there just wasn't time.  We had 55 minutes to present and as it is that wasn't enough.  I ultimately had time to ask 3 questions(one of which was just background), plus 5 shorter audience questions. I was forced to cut out several questions I wanted to ask, but didn't have time to, for those who are interested the major questions I would have liked to ask&lt;br /&gt;were:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;What most often requested feature won't you add to your ORM?&lt;/li&gt;&lt;li&gt;What is the connection between an ORM and a schema migration tool.  Should they both be part of the same project, should they be tied together, or are they totally orthogonal?&lt;/li&gt;&lt;li&gt;What's your support for geographic data?  Is this(or other complex data types like it) in scope for the core of an ORM?&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Despite these difficulties I thought the panel turned out very well.  If there are any other questions about why things were the way they were just ask in the comments and I'll try to post a response.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2086214551143892565?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2086214551143892565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/03/orm-panel-recape.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2086214551143892565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2086214551143892565'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/03/orm-panel-recape.html' title='ORM Panel Recap'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6137698919918407035</id><published>2009-03-30T14:35:00.004-04:00</published><updated>2009-03-30T17:23:28.580-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pycon'/><title type='text'>PyCon Wrapup</title><content type='html'>With PyCon now over for me(and the sprints just begining for those still there) I figured I'd recap it for those who couldn't be there, and compare notes for those who were.  I'll be doing a separate post to cover my panel, since I have quite a brain dump there.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Day One&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We start off the morning with lightning talks, much better than last year due to the removal of so many sponsor talks(thanks Jacob), by now I've already met up with quite a few Django people.  Next I move on to the "Python for CS1" talk, this was particularly interesting for me since they moved away from C++, which is exactly what my school uses.  Next, on to "Introduction to CherryPy" with a few other Djangonauts.  CherryPy looks interesting as a minimalist framework, however a lot of the callback/plugin architecture was confusing me, so I feel like I'd be ignoring it and just being very close to the metal, which isn't always a bad thing, something to explore in the future.  For there I'm off to "How Python is Developed", it's very clear that Django's development model is based of Python's, and that seems to work for both parties.&lt;br /&gt;&lt;br /&gt;Now we head to lunch, which was fantastic, thanks to whoever put it together.  From there I go to "Panel: Python VMs", this was very informative, though I didn't have a chance to ask my question, "Eveyone on the panel has mentioned that performance is likely to improve greatly in the future as a potential reason to use their alternate VM, should Unladen Swallow succeed in their goal, how does that affect you?".  After that I went to the "Twisted, AMQP, and Thrift" talk, it was fairly good, though this feels like a space I have to look at more, or have a real world use case for, to totally understand.&lt;br /&gt;&lt;br /&gt;A quick break, and then we're on to Jesse Noller's mutliprocessing talk.  Jesse really is the expert in this, and having used the multiprocessing library before it's clear to me there's still tons of cool stuff to explore.  Lastly we had the "Behind the scenes of Everyblock.com" from Adrian, it's very cool to see their architecture and it's getting me excited for their going open source in June.  I also got a bit out a shoutout here when Adrian was asked about future scalability and he mentioned sharding across multiple databases.  We had lightning talks and that was the end of the official conference for the day.&lt;br /&gt;&lt;br /&gt;In the evening a big group of us(officially a Pinax meetup, but I think we had more than that) went to a restaurant, as the evening progressed our group shrunk, until they finally kicked us out at closing time.  A good time was had by all I think.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Day two&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;After staying out exceptionally late the night before I ended up sleeping on the floor with friends rather than heading home(thanks!) so, retrospectively, day two should have been tiring, but it was as high energy as could be.  I began the morning with lightning talks followed by Guido's keynote, which was a little odd and jumped around a lot, but I found I really enjoyed it.  After that I got to sit in one room for 3 talks in a row, "The State of Django", "Pinax", and "State of TurboGears", which I can say, without qualifications, were all great(I received another shoutout during the Django talk, I guess I really need to deliver, assuming the GSOC project is accepted).  From there it was off to lunch.&lt;br /&gt;&lt;br /&gt;After lunch it was time to take the stage for my "ORM Panel".  I'm saving all the details for another blog post, but I think it went well.  After this we had a Django Birds of a Feather session, which was very fun.  We got to see Honza Kraal show off the cool features of the Ella admin.  The highlight had to be having Jacob Kalpan-Moss rotate us around the room to get Djangonauts to meet each other.&lt;br /&gt;&lt;br /&gt;After this I heard Jesse Noller's second concurrency talk, and then Ian Bicking's talk on ... stuff, both talks were great, but Ian's is probably in a "you had to be there" category.&lt;br /&gt;&lt;br /&gt;After that it was time for lightning talks, followed by what was supposed to be dinner for people interested in Oebfare(Django blog application) to make some design decisions.  In the end quite a few more people were there and I got to meet some really cool people like Mark Pilgrim.  After dinner it was back to the Hyatt for about an hour of hacking on Django before I called it a night.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;Day Three&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;The final day of the conference.  Once again I began my day with some lightning talks.  After this was the keynote, by the reddit.com creators.  They gave a short, but interesting keynote and answered a lot of questions.  I have to say that some of their technical answers were less than satisfying.  After this I went to an open space on podcasting where I learned that I'm probably the only person who doesn't mind podcasts that are over an hour long.&lt;br /&gt;&lt;br /&gt;After this I went to the Eve Online talk, but I ended up leaving for the testing tools panel.  This panel was interesting, but I can't help but feel that the Python community would be best served by getting the testing tools that everyone agrees about into the unittest module in the Python standard library.  For my last talk of the conference I saw Jacob Kaplan-Moss's talk on "Django's design decisions", thankfully this didn't step on the toes of the ORM design decision panel, and it was very interesting.  From there it was off to lunch, and a final round of lightning talks, capped off my Guido running away with the Django Pony(I hope someone caught this on video), and my conference was done.&lt;br /&gt;&lt;br /&gt;This was a really great conference for me, hopefully everyone else enjoyed it as much as I did.  I'll be doing a follow up post where I answer some of the questions I've seen about the ORM panel, as well as put the rest of my thoughts on paper(hard disk).  And now if you'll excuse me, I have a virtual sprint to attend.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6137698919918407035?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6137698919918407035/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/03/pycon-wrapup.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6137698919918407035'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6137698919918407035'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/03/pycon-wrapup.html' title='PyCon Wrapup'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-4700613857833970322</id><published>2009-03-15T17:06:00.002-04:00</published><updated>2009-03-15T17:11:00.745-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql object'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web2py'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='sql alchemy'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>Google Moderator for PyCon ORM Panel</title><content type='html'>I'm going to be moderating a panel this year at &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;PyCon&lt;/span&gt; between 5 of the Python &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;ORMs&lt;/span&gt;(&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Django&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;SQLAlchemy&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;SQLObject&lt;/span&gt;, Google App Engine, and web2&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;py&lt;/span&gt;).  To make my job easier, and to make sure the most interesting questions are asked I've setup a Google Moderator page for the panel &lt;a href="http://moderator.appspot.com/#15/e=2fa6a&amp;amp;t=30668"&gt;here&lt;/a&gt;.  Go ahead and submit your questions, and moderate others to try to ensure we get the best questions possible, even if you can't make it to &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;PyCon&lt;/span&gt;(there will be a recording made I believe).  I'll be adding my own questions shortly to make sure they are as interesting as I think they are.&lt;br /&gt;&lt;br /&gt;Also, if you aren't already, do try to make it out to &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;PyCon&lt;/span&gt;, there's still time and the talks look to be really exceptional.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-4700613857833970322?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/4700613857833970322/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/03/google-moderator-for-pycon-orm-panel.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4700613857833970322'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4700613857833970322'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/03/google-moderator-for-pycon-orm-panel.html' title='Google Moderator for PyCon ORM Panel'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-4922073499902530313</id><published>2009-02-14T00:05:00.002-05:00</published><updated>2009-02-14T00:08:56.786-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='filter'/><title type='text'>Announcing django-filter</title><content type='html'>&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Django&lt;/span&gt;-filter is a reusable &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Django&lt;/span&gt; application I have been working on for a few weeks, individuals who follow me on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;Github&lt;/span&gt; may have noticed me working on it.  &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Django&lt;/span&gt;-filter is basically an application that creates a generic interface for creating pages like the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;Django&lt;/span&gt; admin &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;changelist&lt;/span&gt; page.  It has support for defining &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;vairous&lt;/span&gt; filters(including custom ones), filtering a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;queryset&lt;/span&gt; based on the user selections, and displaying an HTML form for the filter options.  It has a design based around &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;Django's&lt;/span&gt; &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;ModelForm&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;You can get the code, as well as tests and docs over at &lt;a href="http://github.com/alex/django-filter/tree/master"&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;Github&lt;/span&gt;&lt;/a&gt;.  For all bug reports, comments, and other feedback you can send me a message on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;Github&lt;/span&gt;(or message me on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;IRC&lt;/span&gt;) until I figure out a proper bug tracking system.&lt;br /&gt;&lt;br /&gt;Enjoy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-4922073499902530313?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/4922073499902530313/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/02/announcing-django-filter.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4922073499902530313'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4922073499902530313'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/02/announcing-django-filter.html' title='Announcing django-filter'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7971217431896570400</id><published>2009-02-10T14:32:00.001-05:00</published><updated>2009-02-10T14:33:35.357-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><category scheme='http://www.blogger.com/atom/ns#' term='metaclass'/><title type='text'>A Second Look at Inheritance and Polymorphism with Django</title><content type='html'>Previously I wrote about ways to handle polymorphism with inheritance in Django's ORM in a way that didn't require any changes to your model at all(besides adding in a mixin), today we're going to look at a way to do this that is a little more invasive and involved, but also can provide much better performance.  As we saw previously with no other information we could get the correct subclass for a given object in O(k) queries, where k is the number of subclasses.  This means for a queryset with n items, we would need to do O(nk) queries, not great performance, for a queryset with 10 items, and 3 subclasses we'd need to do 30 queries, which isn't really acceptable for most websites.  The major problem here is that for each object we simply guess as to which subclass a given object is.  However, that's a piece of information we could know concretely if we cached it for later usage, so let's start off there, we're going to be building a mixin class just like we did last time:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db import models&lt;br /&gt;&lt;br /&gt;class InheritanceMixIn(models.Model):&lt;br /&gt;    _class = models.CharField(max_length=100)&lt;br /&gt;    &lt;br /&gt;    class Meta:&lt;br /&gt;        abstract = True&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So now we have a simple abstract model that the base of our inheritance trees can subclass that has a field for caching which subclass we are.  Now let's add a method to actually cache it and retrieve the subclass:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db import models&lt;br /&gt;from django.db.models.fields import FieldDoesNotExist&lt;br /&gt;from django.db.models.related import RelatedObject&lt;br /&gt;&lt;br /&gt;class InheritanceMixIn(models.Model):&lt;br /&gt;    ...&lt;br /&gt;    def save(self, *args, **kwargs):&lt;br /&gt;        if not self.id:&lt;br /&gt;            parent = self._meta.parents.keys()[0]&lt;br /&gt;            subclasses = parent._meta.get_all_related_objects()&lt;br /&gt;            for klass in subclasses:&lt;br /&gt;                if isinstance(klass, RelatedObject) and klass.field.primary_key \&lt;br /&gt;                    and klass.opts == self._meta:&lt;br /&gt;                    self._class = klass.get_accessor_name()&lt;br /&gt;                    break&lt;br /&gt;        return super(InheritanceMixIn, self).save(*args, **kwargs)&lt;br /&gt;    &lt;br /&gt;    def get_object(self):&lt;br /&gt;        try:&lt;br /&gt;            if self._class and self._meta.get_field_by_name(self._class)[0].opts != self._meta:&lt;br /&gt;                return getattr(self, self._class)&lt;br /&gt;        except FieldDoesNotExist:&lt;br /&gt;            pass&lt;br /&gt;        return self&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our save method is where all the magic really happens.  First, we make sure we're only doing this caching if it's the first time a model is being saved.  Then we get the first parent class we have (this means this probably won't play nicely with multiple inheritance, that's unfortunate, but not as common a usecase), then we get all the related objects this class has(this includes the reverse relationship the subclasses have).  Then for each of the subclasses, if it is a RelatedObject, and it is a primary key on it's model, and the class it points to is the same as us then we cache the accessor name on the model, break out, and do the normal save procedure.&lt;br /&gt;&lt;br /&gt;Our get_object function is pretty simple, if we have our class cached, and the model we are cached as isn't of the same type as ourselves we get the attribute with the subclass and return it, otherwise we are the last descendent and just return ourselves.  There is one(possible quite large) caveat here, if our inheritance chain is more than one level deep(that is to say our subclasses have subclasses) then this won't return those objects correctly.  The class is actually cached correctly, but since the top level object doesn't have an attribute by the name of the 2nd level subclass it doesn't return anything.  I believe this can be worked around, but I haven't found a way yet.  One idea would be to actually store the full ancestor chain in the CharField, comma separated, and then just traverse it.&lt;br /&gt;&lt;br /&gt;There is one thing we can do to make this even easier, which is to have instances automatically become the correct subclass when they are pulled in from the DB.  This does have an overhead, pulling in a queryset with n items guarantees O(n) queries.  This can be improved(just as it was for the previous solution) by &lt;a href="http://code.djangoproject.com/ticket/7270"&gt;ticket #7270&lt;/a&gt; which allows select_related to traverse reverse relationships.  In any event, we can write a metaclass to handle this for us automatically:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db import models&lt;br /&gt;from django.db.models.base import ModelBase&lt;br /&gt;from django.db.models.fields import FieldDoesNotExist&lt;br /&gt;from django.db.models.related import RelatedObject&lt;br /&gt;&lt;br /&gt;class InheritanceMetaclass(ModelBase):&lt;br /&gt;    def __call__(cls, *args, **kwargs):&lt;br /&gt;        obj = super(InheritanceMetaclass, cls).__call__(*args, **kwargs)&lt;br /&gt;        return obj.get_object()&lt;br /&gt;&lt;br /&gt;class InheritanceMixIn(models.Model):&lt;br /&gt;    __metaclass__ = InheritanceMetaclass&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here we've created a fairly trivial metaclass that subclasses the default one Django uses for it's models.  The only method we've written is __call__, on a metalcass what __call__ does is handle the instantiation of an object, so it would call __init__.  What we do is do whatever the default __call__ does, so that we get an instances as normal, and then we call the get_object() method we wrote earlier and return it, and that's all.&lt;br /&gt;&lt;br /&gt;We've now looked at 2 ways to handle polymorphism, with this way being more efficient in all cases(ignoring the overhead of having the extra charfield).  However, it still isn't totally efficient, and it fails in several edge cases.  Whether automating the handling of something like this is a good idea is something that needs to be considered on a project by project basis, as the extra queries can be a large overhead, however, they may not be avoidable in which case automating it is probably advantages.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7971217431896570400?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7971217431896570400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7971217431896570400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7971217431896570400'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/02/second-look-at-inheritance-and.html' title='A Second Look at Inheritance and Polymorphism with Django'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8263556827063749447</id><published>2009-01-31T13:05:00.003-05:00</published><updated>2009-01-31T14:15:41.537-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Building a Magic Manager</title><content type='html'>A very common pattern in Django is to create methods on a manager to abstract some usage of ones data.  Some people take a second step and actually create a custom QuerySet subclass with these methods and have their manager proxy these methods to the QuerySet, this pattern is seen in Eric Florenzano's &lt;a href="http://thisweekindjango.com/screencasts/episode/11/django-ground-episode-3/"&gt;Django From the Ground Up&lt;/a&gt; screencast.  However, this requires a lot of repetition, it would be far less verbose if we could just define our methods once and have them available to us on both our managers and QuerySets.&lt;br /&gt;&lt;br /&gt;Django's manager class has one hook for providing the QuerySet, so we'll start with this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db import models&lt;br /&gt;&lt;br /&gt;class MagicManager(models.Manager):&lt;br /&gt;   def get_query_set(self):&lt;br /&gt;       qs = super(MagicManager, self).get_query_set()&lt;br /&gt;       return qs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here we have a very simple get_query_set method, it doesn't do anything but return it's parent's queryset.  Now we need to actually get the methods defined on our class onto the queryset:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class MagicManager(models.Manager):&lt;br /&gt;   def get_query_set(self):&lt;br /&gt;       qs = super(MagicManager, self).get_query_set()&lt;br /&gt;       class _QuerySet(qs.__class__):&lt;br /&gt;           pass&lt;br /&gt;       for method in [attr for attr in dir(self) if not attr.startswith('__') and callable(getattr(self, attr)) and not hasattr(_QuerySet, attr)]:&lt;br /&gt;           setattr(_QuerySet, method, getattr(self, method))&lt;br /&gt;       qs.__class__ = _QuerySet&lt;br /&gt;       return qs&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The trick here is we dynamically create a subclass of whatever class the call to our parent's get_query_set method returns, then we take each attribute on ourself, and if the queryset doesn't have an attribute by that name, and if that attribute is a method then we assign it to our QuerySet subclass.  Finally we set the __class__ attribute of the queryset to be our QuerySet subclass.  The reason this works is when Django chains queryset methods it makes the copy of the queryset have the same class as the current one, so anything we add to our manager will not only be available on the immediately following queryset, but on any that follow due to chaining.&lt;br /&gt;&lt;br /&gt;Now that we have this we can simply subclass it to add methods, and then add it to our models like a regular manager.  Whether this is a good idea is a debatable issue, on the one hand having to write methods twice is a gross violation of Don't Repeat Yourself, however this is exceptionally implicit, which is a major violation of The Zen of Python.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8263556827063749447?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8263556827063749447/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/01/building-magic-manager.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8263556827063749447'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8263556827063749447'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/01/building-magic-manager.html' title='Building a Magic Manager'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5467927657912986930</id><published>2009-01-24T12:04:00.002-05:00</published><updated>2009-01-24T12:08:09.525-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='easy_install'/><category scheme='http://www.blogger.com/atom/ns#' term='release'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax_validation'/><title type='text'>Django Ajax Validation 0.1.0 Released</title><content type='html'>I've just uploaded the first official release of Django Ajax Validation up to PyPi.  You can get it &lt;a href="http://pypi.python.org/pypi/django-ajax-validation/0.1.0"&gt;there&lt;/a&gt;.  For those that don't know it is a reusable Django application that allows you to do JS based validation using you're existing form definitions.  Currently it only works using jQuery.  If there are any problems please let me know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5467927657912986930?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5467927657912986930/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/01/django-ajax-validation-010-released.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5467927657912986930'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5467927657912986930'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/01/django-ajax-validation-010-released.html' title='Django Ajax Validation 0.1.0 Released'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5230939687567230928</id><published>2009-01-19T16:03:00.000-05:00</published><updated>2009-01-19T16:05:06.007-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><title type='text'>Optimizing a View</title><content type='html'>Lately I've been playing with a bit of a fun side project.  I have about a year and half worth of my own chatlogs with friends(and 65,000 messages total) and I've been playing around with them to find interesting statistics.  One facet of my communication with my friends is that we link each other lots of things, and we can always tell when someone is linking something that we've already seen.  So I decided an interesting bit of information would be to see who is the worst offender.&lt;br /&gt;&lt;br /&gt;So we want to write a function that returns the number of items each person has relinked, excluding items they themselves linked.  So I started off with the most simple implementation I could, and this was the end result:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from collections import defaultdict&lt;br /&gt;from operator import itemgetter&lt;br /&gt;&lt;br /&gt;from django.utils.html import word_split_re&lt;br /&gt;&lt;br /&gt;from logger.models import Message&lt;br /&gt;&lt;br /&gt;def calculate_relinks():&lt;br /&gt;    """&lt;br /&gt;    Calculate the number of times each individual has linked something that was&lt;br /&gt;    linked previously in the course of the chat.&lt;br /&gt;    """&lt;br /&gt;    links = defaultdict(int)&lt;br /&gt;    for message in Message.objects.all().order_by('-time').iterator():&lt;br /&gt;        words = word_split_re.split(message.message)&lt;br /&gt;        for word in words:&lt;br /&gt;            if word.startswith('http'):&lt;br /&gt;                if Message.objects.filter(time__lt=message.time).filter(message__contains=word).exclude(speaker=message.speaker).count():&lt;br /&gt;                    links[message.speaker] += 1&lt;br /&gt;    links = sorted(links.iteritems(), key=itemgetter(1), reverse=True)&lt;br /&gt;    return links&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here I iterated over the messages and for each one I went through each of the words and if any of them started with http(the definition of a link for my purposes) I checked to see if this had ever been linked before by someone other than the author of the current message.&lt;br /&gt;&lt;br /&gt;This took about 4 minutes to execute on my dataset, it also executed about 10,000 SQL queries.  This is clearly unacceptable, you can't have a view that takes that long to render, or hits your DB that hard.  Even with aggressive caching this would have been unmaintainable.  Further this algorithm is O(n**2) or thereabouts so as my dataset grows this would have gotten worse exponentially.&lt;br /&gt;&lt;br /&gt;By changing this around however I was able to achieve far better results:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from collections import defaultdict&lt;br /&gt;from operator import itemgetter&lt;br /&gt;&lt;br /&gt;from django.utils.html import word_split_re&lt;br /&gt;&lt;br /&gt;from logger.models import Message&lt;br /&gt;&lt;br /&gt;def calculate_relinks():&lt;br /&gt;    """&lt;br /&gt;    Calculate the number of times each individual has linked something that was&lt;br /&gt;    linked previously in the course of the chat.&lt;br /&gt;    """&lt;br /&gt;    links = defaultdict(set)&lt;br /&gt;    counts = defaultdict(int)&lt;br /&gt;    for message in Message.objects.all().filter(message__contains="http").order_by('time').iterator():&lt;br /&gt;        words = word_split_re.split(message.message)&lt;br /&gt;        for word in words:&lt;br /&gt;            if word.startswith('http'):&lt;br /&gt;                if any([word in links[speaker] for speaker in links if speaker != message.speaker]):&lt;br /&gt;                    counts[message.speaker] += 1&lt;br /&gt;                links[message.speaker].add(word)&lt;br /&gt;    counts = sorted(counts.iteritems(), key=itemgetter(1), reverse=True)&lt;br /&gt;    return counts&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here what I do is go through each of the messages which contain the string "http"(this is already a huge advantage since that means we process about 1/6 of the messages in Python that we originally were), for each message we go through each of the words in it, and for each that is a link we check if any other person has said it by looking in the caches we maintain in Python, and if they do we increment their count, finally we add the link to that persons cache.&lt;br /&gt;&lt;br /&gt;By comparison this executes in .3 seconds, executes only 1 SQL query, and it will scale linearly(as well as is possible).  For reference both of these functions are compiled using Cython.  This ultimately takes almost no work to do and for computationally heavy operations this can provide a huge boon.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5230939687567230928?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5230939687567230928/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/01/optimizing-view.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5230939687567230928'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5230939687567230928'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/01/optimizing-view.html' title='Optimizing a View'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7456180399514762415</id><published>2009-01-14T21:13:00.003-05:00</published><updated>2009-01-14T21:29:25.121-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><title type='text'>New Admin URLs</title><content type='html'>I'm very happy to announce that with &lt;a href="http://code.djangoproject.com/changeset/9739"&gt;revision 9739&lt;/a&gt; of &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Django&lt;/span&gt; the admin now uses normal URL resolvers and its URLs can be reversed.  This is a tremendous improvement over the previous ad &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;hoc&lt;/span&gt; system and it gives users the distinct advantage of being able to reverse the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;admin's&lt;/span&gt; URLs.  However, in order to make this work a new feature went into the URL resolving system that any user can use in their own code.&lt;br /&gt;&lt;br /&gt;Specifically users can now have objects which provide URLs.  Basically this is because include() can now accept any &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;iterable&lt;/span&gt;, rather than just a string which points to a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;urlconf&lt;/span&gt;.  To get an idea of what this looks like you can look at &lt;a href="http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/sites.py#L151"&gt;the way the admin now does it&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;There are going to be a few more great additions to &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;Django&lt;/span&gt; going in as we move towards the 1.1 alpha so keep an eye out for them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7456180399514762415?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7456180399514762415/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/01/new-admin-urls.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7456180399514762415'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7456180399514762415'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/01/new-admin-urls.html' title='New Admin URLs'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3072183876006251408</id><published>2009-01-03T02:35:00.003-05:00</published><updated>2009-01-03T02:43:19.024-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>2008 and 2009</title><content type='html'>Eric Florenzano recently wrote an excellent post titled, &lt;a href="http://www.eflorenzano.com/blog/post/2008-review-2009-goals/"&gt;"2008 in Review &amp;amp; 2009 Goals"&lt;/a&gt;.  I am now going to blantly steal his idea.&lt;br /&gt;&lt;br /&gt;2008:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Joined Twitter&lt;/li&gt;&lt;li&gt;Voted for the first time&lt;/li&gt;&lt;li&gt;Had a blast at PyCon&lt;/li&gt;&lt;li&gt;Wrote a reusable Django application and open sourced it&lt;/li&gt;&lt;li&gt;Learned git&lt;/li&gt;&lt;li&gt;Got accepted at, and began studying Computer Science at RPI&lt;/li&gt;&lt;li&gt;Wrote a blog post every day for 30 days in a row&lt;/li&gt;&lt;li&gt;Started work on my programming language, Al&lt;/li&gt;&lt;li&gt;Contributed heavily to Django&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Started a blog&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Proposed a panel for PyCon 2009 and was accepted&lt;/li&gt;&lt;/ul&gt;And probably quite a bit more.&lt;br /&gt;&lt;br /&gt;2009:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Continue work on Al, bring it up to a usable state&lt;/li&gt;&lt;li&gt;Continue writing on my blog&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Propose a talk for another conference&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Continue contributing to Django&lt;/li&gt;&lt;li&gt;Become a committer on one of the projects I use&lt;/li&gt;&lt;li&gt;Do something interesting over the summer, hopefully a cool internship or Google Summer of Code&lt;/li&gt;&lt;li&gt;Do well in class&lt;/li&gt;&lt;/ul&gt;In light of all the stuff that happened in 2008 I hope these are reasonable goals.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3072183876006251408?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3072183876006251408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2009/01/2008-and-2009.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3072183876006251408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3072183876006251408'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2009/01/2008-and-2009.html' title='2008 and 2009'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6764381485013787395</id><published>2008-12-28T03:46:00.003-05:00</published><updated>2008-12-28T03:57:07.478-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='forms'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><title type='text'>Building a Read Only Field in Django</title><content type='html'>One commonly requested feature in Django is to have a field on a form(or in the admin), that is read only.  Such a thing is going may be a Django 1.1 feature for the admin, exclusively, since this is the level that it makes sense at, a form is by definition for inputing data, not displaying data.  However, it is still possible to do this with Django now, and it doesn't even take very much code.  As I've said, doing it in this manner(as a form field) isn't particularly intuitive or sensible, however it is possible.&lt;br /&gt;&lt;br /&gt;The first thing we need to examine is how we would want to use this, for our purposes we'll use this just like we would a normal field on a form:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django import forms&lt;br /&gt;from django.contrib.auth.models import User&lt;br /&gt;&lt;br /&gt;class UserForm(forms.ModelForm):&lt;br /&gt;    email = ReadOnlyField()&lt;br /&gt;    &lt;br /&gt;    class Meta:&lt;br /&gt;        model = User&lt;br /&gt;        fields = ['email', 'username']&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So we need to write a field, our field will actually need to be a subclass of FileField, at first glance this makes absolutely no sense, our field isn't taking files, it isn't taking any data at all.  However FileFields receive the initial data for their clean() method, which other fields don't, and we need this behavior for our field to work:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class ReadOnlyField(forms.FileField):&lt;br /&gt;    widget = ReadOnlyWidget&lt;br /&gt;    def __init__(self, widget=None, label=None, initial=None, help_text=None):&lt;br /&gt;        forms.Field.__init__(self, label=label, initial=initial, &lt;br /&gt;            help_text=help_text, widget=widget)&lt;br /&gt;    &lt;br /&gt;    def clean(self, value, initial):&lt;br /&gt;        self.widget.initial = initial&lt;br /&gt;        return initial&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see in the clean method we are exploiting this feature in order to give our widget the initial value, which it normally won't have access to at render time.&lt;br /&gt;&lt;br /&gt;Now we write our ReadOnlyWidget:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.forms.util import flatatt&lt;br /&gt;&lt;br /&gt;class ReadOnlyWidget(forms.Widget):&lt;br /&gt;    def render(self, name, value, attrs):&lt;br /&gt;        final_attrs = self.build_attrs(attrs, name=name)&lt;br /&gt;        if hasattr(self, 'initial'):&lt;br /&gt;            value = self.initial&lt;br /&gt;        return "&lt;p%s&gt;%s&lt;/p&gt;" % (flatatt(final_attrs), value or '')&lt;br /&gt;    &lt;br /&gt;    def _has_changed(self, initial, data):&lt;br /&gt;        return False&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our widget simply renders the initial value to a p tag, instead of as an input tag.  We also override the _has_changed method to always return False, this is used in formsets to avoid resaving data that hasn't changed, since our input can't change data, obviously it won't change.&lt;br /&gt;&lt;br /&gt;And that's all there is to it, less than 25 lines of code in all.  As I said earlier this is a fairly poor architecture, and I wouldn't recommend it, however it does work and serves as proof that Django will allow you to do just about anything in you give in a try.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6764381485013787395?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6764381485013787395/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/building-read-only-field-in-django.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6764381485013787395'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6764381485013787395'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/building-read-only-field-in-django.html' title='Building a Read Only Field in Django'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6626854064322574323</id><published>2008-12-25T19:45:00.003-05:00</published><updated>2008-12-25T20:02:52.123-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='template'/><title type='text'>Building a Function Templatetag</title><content type='html'>One of the complaints often lobbied against Django's templating system is that there is no way to create functions.  This is intentional, Django's template language is not meant to be a full programming language.  However, if one wants to it is entirely possible to build a templatetag to allow a user to create, and call functions in the template language.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;To get started we need to build a tag to create functions, first we need our parsing function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django import template&lt;br /&gt;&lt;br /&gt;register = template.Library()&lt;br /&gt;&lt;br /&gt;@register.tag&lt;br /&gt;def function(parser, token):&lt;br /&gt;    arglist = token.split_contents()[1:]&lt;br /&gt;    func_name = arglist.pop(0)&lt;br /&gt;    nodelist = parser.parse(('endfunction',))&lt;br /&gt;    parser.delete_first_token()&lt;br /&gt;    return FunctionNode(func_name, arglist, nodelist)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The format for our tag is going to be {% function func_name arglist... %}.  So we parse this out of the token.  We get everything after the initial piece of the function definition, we make the first part of this the function name, and the rest is the argument list.  The next part is to parse until the {% endfunction %} tag.  Finally we return a FunctionNode.  Now we need to actually build this node:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class FunctionNode(template.Node):&lt;br /&gt;    def __init__(self, func_name, arglist, nodelist):&lt;br /&gt;        self.func_name = func_name&lt;br /&gt;        self.nodelist = nodelist&lt;br /&gt;        self.arglist = arglist&lt;br /&gt;    &lt;br /&gt;    def render(self, context):&lt;br /&gt;        if '_functions' not in context:&lt;br /&gt;            context['_functions'] = {}&lt;br /&gt;        context['_functions'][self.func_name] = (self.arglist, self.nodelist)&lt;br /&gt;        return ''&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Our __init__ method just stores the data on the instance.  Our render method stores the argument list and the nodes that make up the function in the context.  This gives us all the information we will need to know in order to call and render our functions.  Now we need our actual mechanism for calling our functions:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;@register.tag&lt;br /&gt;def call(parser, token):&lt;br /&gt;    arglist = token.split_contents()[1:]&lt;br /&gt;    func_name = arglist.pop(0)&lt;br /&gt;    return CallNode(func_name, arglist)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Like our function tag, we parse out the name of the function, and then the argument list.  And now we need to render the result of calling it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class CallNode(template.Node):&lt;br /&gt;    def __init__(self, func_name, arglist):&lt;br /&gt;        self.func_name = func_name&lt;br /&gt;        self.arglist = arglist&lt;br /&gt;    &lt;br /&gt;    def render(self, context):&lt;br /&gt;        arglist, nodelist = context['_functions'][self.func_name]&lt;br /&gt;        c = template.Context(dict(zip(arglist, [template.Variable(x).resolve(context) for x in self.arglist])))&lt;br /&gt;        return nodelist.render(c)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;render gets the arglist and nodelist out of the context, and then creates a context of the calling variables, by zipping together the variable names from function definition and function calling.&lt;br /&gt;&lt;br /&gt;Now we can create functions by doing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;{% function f arg %}&lt;br /&gt;    {{ arg }}&lt;br /&gt;{% endfunction %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And call them by doing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;{% call f some_var %}&lt;br /&gt;{% call f some_other_var %}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hopefully this has given you a useful insight into how to build powerful templatetags in Django's template language.  One possible improvement the reader may want to do is to have the function tag actually register a templatetag out of the function definition, and then be able to use it by simpling using it like a normal templatetag.  As a slight warning I haven't tested this with a wide range of data, so if there are any issues please report them.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6626854064322574323?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6626854064322574323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/building-function-templatetag.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6626854064322574323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6626854064322574323'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/building-function-templatetag.html' title='Building a Function Templatetag'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7791078008100792857</id><published>2008-12-25T19:37:00.002-05:00</published><updated>2008-12-25T19:45:04.994-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='deployment'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='webfaction'/><title type='text'>Many Thanks to Webfaction</title><content type='html'>One of my recent projects has been to build a website for someone so that they could put their portfolio online.  Building the site with Django, was of course, a snap.  However, I would like to take a moment to thank &lt;a href="http://www.webfaction.com/"&gt;Webfaction&lt;/a&gt; for the painless deployment.  It's a small website, so shared hosting is perfect for it, and Webfaction made it a breeze.  Setting up a PostgreSQL database was a snap.  Getting Django installed and my code on the server was easy.  My one pain point was of my own creation(I broke the PythonPath setting in the WSGI file Webfaction provides), but even with that deployment didn't take more than an hour.  They even made it easy to use the ultralight weight Nginx for my static files.&lt;br /&gt;&lt;br /&gt;All said, Webfaction was exactly what I needed from a host, and they delivered.  For the curious parties you can see the site &lt;a href="http://www.caseyhoogstraten.com/"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7791078008100792857?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7791078008100792857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/many-thanks-to-webfaction.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7791078008100792857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7791078008100792857'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/many-thanks-to-webfaction.html' title='Many Thanks to Webfaction'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1612696426132641675</id><published>2008-12-15T19:10:00.002-05:00</published><updated>2008-12-15T19:26:11.623-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql object'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='web2py'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='sql alchemy'/><category scheme='http://www.blogger.com/atom/ns#' term='gae'/><title type='text'>PyCon '09, Here I come!</title><content type='html'>This past year I attended PyCon 2008 in Chicago, which was a tremendous conference.  I had a chance to meet people I knew from the community, listen to some amazing talks, meet new people, and get to sprint.  As a result of this tremendous experience I decided for this year to submit a talk proposal.  I found out just a few minutes ago that my proposal has been accepted.&lt;br /&gt;&lt;br /&gt;I proposed a panel on "Object Relational Mapper Philosophies and Design Decisions".  This panel is going to look at the design decisions that each of several ORMs engaged, and what philosophies they had, but with respect to their public APIs and their internal code design.  Participating in the panel will be:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Jacob Kaplan-Moss, representing Django&lt;/li&gt;&lt;li&gt;Ian Bicking, representing SQL Object&lt;/li&gt;&lt;li&gt;Mike Bayer, represening SQL Alchemy&lt;/li&gt;&lt;li&gt;Guido van Rossum, representing Google App Engine&lt;/li&gt;&lt;li&gt;Dr. Massimo Di Pierro, representing web2py&lt;/li&gt;&lt;/ul&gt;I'm tremendously honored to be able to moderate a panel at PyCon, especially with these five individuals.  They are all indcredibly smart, and they each bring a different insight and perspective to this panel.&lt;br /&gt;&lt;br /&gt;PyCon is a great conference and I would encourage anyone who can to attend.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1612696426132641675?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1612696426132641675/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/pycon-09-here-i-come.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1612696426132641675'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1612696426132641675'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/pycon-09-here-i-come.html' title='PyCon &apos;09, Here I come!'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2398030335968933484</id><published>2008-12-05T16:24:00.002-05:00</published><updated>2008-12-05T16:49:25.082-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Playing with Polymorphism in Django</title><content type='html'>One of the most common requests of people using inheritance in Django, is to have the a queryset from the baseclass return instances of the derives model, instead of those of the baseclass, as you might see with polymorphism in other languages.  This is a leaky abstraction of the fact that our Python classes are actually representing rows in separate tables in a database.  Django itself doesn't do this, because it would require expensive joins across all derived tables, which the user probably doesn't want in all situations.  For now, however, we can create a function that given an instance of the baseclass returns an instance of the appropriate subclass, be aware that this will preform up to k queries, where k is the number of subclasses we have.&lt;br /&gt;&lt;br /&gt;First let's set up some test models to work with:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db import models&lt;br /&gt;&lt;br /&gt;class Place(models.Model):&lt;br /&gt;    name = models.CharField(max_length=50)&lt;br /&gt;&lt;br /&gt;    def __unicode__(self):&lt;br /&gt;        return u"%s the place" % self.name&lt;br /&gt;    &lt;br /&gt;&lt;br /&gt;class Restaurant(Place):&lt;br /&gt;    serves_pizza = models.BooleanField()&lt;br /&gt;    &lt;br /&gt;    def __unicode__(self):&lt;br /&gt;        return "%s the restaurant" % self.name&lt;br /&gt;&lt;br /&gt;class Bar(Place):&lt;br /&gt;    serves_wings = models.BooleanField()&lt;br /&gt;&lt;br /&gt;    def __unicode__(self):&lt;br /&gt;        return "%s the bar" % self.name&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;These are some fairly simple models that represents a common inheritance pattern.  Now what we want to do is be able to get an instance of the correct subclass for a given instance of Place.  To do this we'll create a mixin class, so that we can use this with other classes.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class InheritanceMixIn(object):&lt;br /&gt;    def get_object(self):&lt;br /&gt;        ...&lt;br /&gt;&lt;br /&gt;class Place(models.Model, InheritanceMixIn):&lt;br /&gt;    ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So what do we need to do in our get_object method?  Basically we need to loop each of the subclasses, try to get the correct attribute and return it if it's there, if none of them are there, we should just return ourself.  We start by looping over the fields:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class InheritanceMixIn(object):&lt;br /&gt;    def get_object(self):&lt;br /&gt;        for f in self._meta.get_all_field_names():&lt;br /&gt;            field = self._meta.get_field_by_name(f)[0]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;_meta is where Django stores lots of the internal data about a mode, so we get all of the field names, this includes the names of the reverse descriptors that related models provide.  Then we get the actual field for each of these names.  Now that we have each of the fields we need to test if it's one of the reverse descriptors for the subclasses:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db.models.related import RelatedObject&lt;br /&gt;&lt;br /&gt;class InheritanceMixIn(object):&lt;br /&gt;    def get_object(self):&lt;br /&gt;        for f in self._meta.get_all_field_names():&lt;br /&gt;            field = self._meta.get_field_by_name(f)[0]&lt;br /&gt;            if isinstance(field, RelatedObject) and field.field.primary_key:&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We first test if the field is a RelatedObject, and if it we see if the field on the other model is a primary key, which it will be if it's a subclass(or technically any one to one that is a primary key).  Lastly we need to find what the name of that attribute is on our model and to try to return it:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class InheritanceMixIn(object):&lt;br /&gt;    def get_object(self):&lt;br /&gt;        for f in self._meta.get_all_field_names():&lt;br /&gt;            field = self._meta.get_field_by_name(f)[0]&lt;br /&gt;            if isinstance(field, RelatedObject) and field.field.primary_key:&lt;br /&gt;                try:&lt;br /&gt;                    return getattr(self, field.get_accessor_name())&lt;br /&gt;                except field.model.DoesNotExist:&lt;br /&gt;                    pass&lt;br /&gt;        return self&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We try to return the attribute, and if it raises a DoesNotExist exception we move on to the next one, if none of them return anything, we just return ourself.&lt;br /&gt;&lt;br /&gt;And that's all it takes.  This won't be super efficient, since for a queryset of n objects, this will take O(n*k) given k subclasses.  &lt;a href="http://code.djangoproject.com/ticket/7270"&gt;Ticket 7270&lt;/a&gt; deals with allowing select_related() to work across reverse one to one relations as well, which will allow one to optimise this, since the subclasses would already be gotten from the database.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2398030335968933484?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2398030335968933484/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/playing-with-polymorphism-in-django.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2398030335968933484'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2398030335968933484'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/playing-with-polymorphism-in-django.html' title='Playing with Polymorphism in Django'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3412543330746260244</id><published>2008-12-02T20:17:00.002-05:00</published><updated>2008-12-02T20:23:16.390-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>A month in review</title><content type='html'>Today is the last day of blogging every day for a month.  It was harder than I expected, but I ultimately made it, which also surprised me.  For my last post(of the month) I figured I'd share some stats about the blog from the month.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;About 10k visitors and 13k page views&lt;/li&gt;&lt;li&gt;My 3 post popular posts were "What Python learned from Economics", "How the heck do Django Models Work", and "A Timeline View in Django"&lt;/li&gt;&lt;li&gt;My top 3 referers were reddit, ycombinator, and the Django project site.&lt;/li&gt;&lt;li&gt;Top search keyword was "django models"&lt;/li&gt;&lt;li&gt;2/3 of my viewers were using Firefox&lt;/li&gt;&lt;li&gt;Under 3% IE(yay)&lt;/li&gt;&lt;li&gt;28% were Linux users&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;I don't think I'll ever blog this frequently again(at least not until next November), however I hope to continue writing fairly frequently and I have at least two more posts planned for December.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3412543330746260244?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3412543330746260244/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/month-in-review.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3412543330746260244'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3412543330746260244'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/month-in-review.html' title='A month in review'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-197607892057718209</id><published>2008-12-01T21:42:00.003-05:00</published><updated>2008-12-01T22:24:48.166-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='foreignkey'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Fixing up our identity mapper</title><content type='html'>The past two days we've been looking at building an identity mapper in Django.  Today we're going to implement some of the improvements I mentioned yesterday.&lt;br /&gt;&lt;br /&gt;The first improvement we're going to do is it have it execute the query as usual and just cache the results, to prevent needing to execute additional queries.  This means changing the __iter__ method on our queryset class:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    def __iter__(self):&lt;br /&gt;        for obj in self.iterator():&lt;br /&gt;            try:&lt;br /&gt;                yield get_from_cache(self.model, obj.pk)&lt;br /&gt;            except KeyError:&lt;br /&gt;                cache_instance(obj)&lt;br /&gt;                yield obj&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we just iterate over self.iterator() which is a slightly lower level interface to a querysets iteration, it bypasses all the caching that occurs(this means that for now at least, if we iterate over our queryset twice we actually execute two queries, whereas Django would normally do just one), however overall this will be a big win, since before if an item wasn't in the cache we would do an extra query for it.&lt;br /&gt;&lt;br /&gt;The next improvement I proposed was to use Django's built in caching interfaces.  However, this won't work, this is because the built in locmem cache backend pickles and unpickles everything before caching and retrieving everything from the cache, so we'd end up with different objects(which defeats the point of this).&lt;br /&gt;&lt;br /&gt;The last improvement we can make is to have this work on related objects for which we already know the primary key.  The obvious route to do this is to start hacking in django.db.models.fields.related, however as I've mentioned in a previous post this area of the code is a bit complex, however if we know a little bit about how this query is executed we can do the optimisation in a far simpler way.  As it turns out the related object descriptor simply tries to do the query using the default manager's get method.  Therefore, we can simply special case this occurrence in order to optimise this.  We also have to make a slight chance to our manager, as by default the manager won't be used on related object queries:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class CachingManager(Manager):&lt;br /&gt;    use_for_related_fields = True&lt;br /&gt;    def get_query_set(self):&lt;br /&gt;        return CachingQuerySet(self.model)&lt;br /&gt;&lt;br /&gt;class CachingQueryset(QuerySet):&lt;br /&gt;    ...&lt;br /&gt;    def get(self, *args, **kwargs):&lt;br /&gt;        if len(kwargs) == 1:&lt;br /&gt;            k = kwargs.keys()[0]&lt;br /&gt;            if k in ('pk', 'pk__exact', '%s' % self.model._meta.pk.attname, '%s__exact' % self.model._meta.pk.attname):&lt;br /&gt;                try:&lt;br /&gt;                    return get_from_cache(self.model, kwargs[k])&lt;br /&gt;                except KeyError:&lt;br /&gt;                    pass&lt;br /&gt;        clone = self.filter(*args, **kwargs)&lt;br /&gt;        objs = list(clone[:2])&lt;br /&gt;        if len(objs) == 1:&lt;br /&gt;            return objs[0]&lt;br /&gt;        if not objs:&lt;br /&gt;            raise self.model.DoesNotExist("%s matching query does not exist."&lt;br /&gt;                             % self.model._meta.object_name)&lt;br /&gt;        raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"&lt;br /&gt;                % (self.model._meta.object_name, len(objs), kwargs))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see we just add one line to the manager, and a few lines to the begging of the get() method.  Basically our logic is if there is only one kwarg to the get() method, and it is a query on the primary key of the model, we try to return our cached instance.  Otherwise we fall back to executing the query.&lt;br /&gt;&lt;br /&gt;And with this we've improved the efficiency of our identity map, there are almost definitely more places for optimisations, but now we have an identity map in very few lines of code.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-197607892057718209?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/197607892057718209/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/fixing-up-our-identity-mapper.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/197607892057718209'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/197607892057718209'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/fixing-up-our-identity-mapper.html' title='Fixing up our identity mapper'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8798399753871300641</id><published>2008-12-01T02:09:00.002-05:00</published><updated>2008-12-01T02:20:37.537-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='foreignkey'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>A Few More Thoughts on the Identity Mapper</title><content type='html'>It's late, and I've my flight was delayed for several hours so today is going to be another quick post.  With that note here are a few thoughts on the identity mapper:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;We can optimize it to actually execute fewer queries by having it run the query as usual, and then use the primary key to check the cache, else cache the instance we already have.&lt;/li&gt;&lt;li&gt;As Doug points out in the comments, there are built in caching utilities in Django we should probably be taking advantage of.  The only qualification is that whatever cache we use needs to be in memory and in process.&lt;/li&gt;&lt;li&gt;The cache is actually going to be more efficient than I originally thought.  On a review of the source the default manager is used for some related queries, so our manager will actually be used for those.&lt;/li&gt;&lt;li&gt;The next place to optimize will actually be on single related objects(foreign keys and one to ones).  That's because we already have their primary key and so we can check for them in the cache without executing any SQL queries.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;And lastly a small note.  As you may have noticed I've been doing the National Blog Everyday for a Month Month, since I started two days late, I'm going to be continueing on for another two days.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8798399753871300641?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8798399753871300641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/12/few-more-thoughts-on-identity-mapper.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8798399753871300641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8798399753871300641'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/12/few-more-thoughts-on-identity-mapper.html' title='A Few More Thoughts on the Identity Mapper'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2631332073138411752</id><published>2008-11-29T15:10:00.004-05:00</published><updated>2008-11-29T15:41:39.353-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Building a simple identity map in Django</title><content type='html'>In Django's ticket tracker lies &lt;a href="http://code.djangoproject.com/ticket/17"&gt;ticket 17&lt;/a&gt;, the second oldest open ticket, this proposes an optimisation to have instances of the same database object be represented by the same object in Python, essentially that means for this code:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;a = Model.objects.get(pk=3)&lt;br /&gt;b = Model.objects.get(pk=3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;a and b would be the same object at the memory level.  This can represent a large optimisation in memory usage if you're application has the potential to have duplicate objects(for example, related objects).  It is possible to implement a very simple identity map without touching the Django source at all.&lt;br /&gt;&lt;br /&gt;The first step is to set up some very basic infastructure, this is going to be almost identical to what Eric Florenzano does in his post, &lt;a href="http://www.eflorenzano.com/blog/post/drop-dead-simple-django-caching/"&gt;"Drop-dead simple Django caching"&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;We start with a few helper functions:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;_CACHE = {}&lt;br /&gt;&lt;br /&gt;def key_for_instance(obj, pk=None):&lt;br /&gt;    if pk is None:&lt;br /&gt;        pk = obj.pk&lt;br /&gt;    return "%s-%s-%s" % (obj._meta.app_label, obj._meta.module_name, pk)&lt;br /&gt;&lt;br /&gt;def get_from_cache(klass, pk):&lt;br /&gt;    return _CACHE[key_for_instance(klass, pk)]&lt;br /&gt;&lt;br /&gt;def cache_instance(instance):&lt;br /&gt;    _CACHE[key_for_instance(instance)] = instance&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We create our cache, which is a Python dictionary, a function to generate the cache key for an object, a function to get an item from the cache, and a function to cache an item.  How these work should be pretty simple.  Next we need to create some functions to make sure objects get update in the cache.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db.models.signals import post_save, pre_delete&lt;br /&gt;&lt;br /&gt;def post_save_cache(sender, instance, **kwargs):&lt;br /&gt;    cache_instance(instance)&lt;br /&gt;post_save.connect(post_save_cache)&lt;br /&gt;&lt;br /&gt;def pre_delete_uncache(sender, instance, **kwargs):&lt;br /&gt;    try:&lt;br /&gt;        del _CACHE[key_for_instance(instance)]&lt;br /&gt;    except KeyError:&lt;br /&gt;        pass&lt;br /&gt;pre_delete.connect(pre_delete_uncache)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here we set up two signal receivers, when an object is saved we cache it, and when one is deleted we remove it from the cache.&lt;br /&gt;&lt;br /&gt;Now we want a way to use our cache the way we already use our connection to the database, this means implementing some sort of hook in a QuerySet, this looks like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;from django.db.models.query import QuerySet&lt;br /&gt;&lt;br /&gt;class CachingQueryset(QuerySet):&lt;br /&gt;    def __iter__(self):&lt;br /&gt;        obj = self.values_list('pk', flat=True)&lt;br /&gt;        for pk in obj:&lt;br /&gt;            try:&lt;br /&gt;                yield get_from_cache(self.model, pk)&lt;br /&gt;            except KeyError:&lt;br /&gt;                instance = QuerySet(self.model).get(pk=pk)&lt;br /&gt;                cache_instance(instance)&lt;br /&gt;                yield instance&lt;br /&gt;    &lt;br /&gt;    def get(self, *args, **kwargs):&lt;br /&gt;        clone = self.filter(*args, **kwargs)&lt;br /&gt;        objs = list(clone[:2])&lt;br /&gt;        if len(objs) == 1:&lt;br /&gt;            return objs[0]&lt;br /&gt;        if not objs:&lt;br /&gt;            raise self.model.DoesNotExist("%s matching query does not exist."&lt;br /&gt;                             % self.model._meta.object_name)&lt;br /&gt;        raise self.model.MultipleObjectsReturned("get() returned more than one %s -- it returned %s! Lookup parameters were %s"&lt;br /&gt;                % (self.model._meta.object_name, len(objs), kwargs))&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We create a subclass of QuerySet and override it's __iter__() and get() methods.  By default __iter__ does a fair bit of heavy lifting to internally cache the results and allow the usage of multiple iterators properly.  We override this to do something simpler.  We get the primary keys of each item in the queryset and iterate over them, if the object is in the cache we return it, otherwise we execute a database query to get it, and then cache it.  We also override get() to make sure it makes use of the caching we just set up.&lt;br /&gt;&lt;br /&gt;To use this on a model we need to create a simple manager:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class CachingManager(Manager):&lt;br /&gt;    def get_query_set(self):&lt;br /&gt;        return CachingQuerySet(self.model)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And then we can use this with our models:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;class Post(models.Model):&lt;br /&gt;    title = models.CharField(max_length=100)&lt;br /&gt;    &lt;br /&gt;    objects = CachingManager()&lt;br /&gt;&lt;br /&gt;Post.objects.all()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now all Posts accessed within the same thread will be cached using the strategy we've implemented.&lt;br /&gt;&lt;br /&gt;This strategy will not save us database queries, indeed in some cases it can result in many more queries, it is designed to save memory usage(and be implemented as simply as possible).  It can also be made far more useful by having related objects use this strategy as well(if Post had a foreign key to author it would be nice to have all post authors share the same instances, since even you have a large queryset of Posts were all the Posts are unique, they are likely to have duplicate authors).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2631332073138411752?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2631332073138411752/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/building-simple-identity-map-in-django.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2631332073138411752'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2631332073138411752'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/building-simple-identity-map-in-django.html' title='Building a simple identity map in Django'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1841919081256087787</id><published>2008-11-29T01:58:00.003-05:00</published><updated>2008-11-29T02:03:45.219-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Other ORM Goodies</title><content type='html'>In addition to the aggregate work, the GSOC student had time to finish &lt;a href="http://code.djangoproject.com/ticket/7210"&gt;ticket 7210&lt;/a&gt;, which adds support for expressions to filter() and update().  This means you'll be able to execute queries in the form of:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;SELECT * FROM table WHERE height &gt; width;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or similar UPDATE queries.  This has a syntax similar to that of Q objects, using a new F object.  So the above query would look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Model.objects.filter(height__gt=F('width'))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;or an update query could look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Employee.objects.update(salary=F('salary')*1.1)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;these objects support the full range of arithmetic operations.  These are slated to be a part of Django 1.1.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1841919081256087787?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1841919081256087787/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/other-orm-goodies.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1841919081256087787'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1841919081256087787'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/other-orm-goodies.html' title='Other ORM Goodies'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-4720446671338974072</id><published>2008-11-27T12:09:00.002-05:00</published><updated>2008-11-27T12:38:22.719-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>What aggregates are going to look like</title><content type='html'>Prior to Django 1.0 there was a lot of discussion of what the syntax for doing aggregate queries would like.  Eventually a syntax was more or less agreed upon, and over the summer Nicolas Lara implemented this for the Google Summer of Code project, mentored by Russell Keith-Magee.  This feature is considered a blocker for Django 1.1, so I'm going to outline what the syntax for these aggregates will be.&lt;br /&gt;&lt;br /&gt;To facillitate aggregates two new methods are being added to the queryset, aggregate and annotate.  Aggregate is used to preform basic aggregation on queryset itself, for example getting the MAX, MIN, AVG, COUNT, and SUM for a given field on the model.   Annotate is used for getting information about a related model.&lt;br /&gt;&lt;br /&gt;For example, if we had a product model with a price field, we could get the max and minimum price for a product by doing the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Product.objects.aggregate(Min('price'), Max('price'))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;this will return something like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;{'price__min': 23.45,&lt;br /&gt; 'price__max': 47.89,&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;We can also give the results aliases, so it's easier to read(if no alias is provided it fallsback to using fieldname__aggregate:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Product.objects.aggregate(max_price = Max('price'), min_price = Min('price'))&lt;br /&gt;{'min_price': 23.45,&lt;br /&gt; 'max_price': 47.89,&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You can also do aggregate queries on related fields, but the idea is the same, return a single value for each aggregate.&lt;br /&gt;&lt;br /&gt;In my opinion, annotate queries are far more interesting.  Annotate queries let us represent queries such as, "give me all of the Tags that more than 3 objects have been tagged with", which would look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Tag.objects.annotate(num_items=Count('tagged')).filter(num_items__gt=3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This would return a normal queryset where each Tag object has an attribute named num_items, that was the Count() of all of tagged for it(I'm assuming tagged is a reverse foreign key, to a model that represents a tagged relationship).  Another query we might want to execute would be to see how many awards authors of each author's publisher had won, this would look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;Author.objects.annotate(num_publisher_awards=Count('publisher__authors__awards')).order_by('num_publisher_awards')&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is a little more complicated, but just like when using filter() we can chain this __ syntax.  Also, as you've probably noticed we can filter and order_by these annotated attributes the same as we can with regular fields.&lt;br /&gt;&lt;br /&gt;If you're interested in seeing more of how this works, Nicolas Lara has written some documentation and doc tests that you can see &lt;a href="http://code.google.com/p/django-aggregation/w/list"&gt;here&lt;/a&gt;.  For now none of this is in the Django source tree yet, but there is a patch with the latest work on &lt;a href="http://code.djangoproject.com/ticket/3566"&gt;ticket 366&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Happy thanksgiving!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-4720446671338974072?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/4720446671338974072/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-aggregates-are-going-to-look-like.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4720446671338974072'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4720446671338974072'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-aggregates-are-going-to-look-like.html' title='What aggregates are going to look like'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-4933265100914673981</id><published>2008-11-27T02:02:00.002-05:00</published><updated>2008-11-27T02:05:14.530-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>Some thoughts on Blogging</title><content type='html'>As we come to the close of November, I thought&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; I'd take a comment to reflect on blogging.  For one, this is by far more writing than I've ever done.  Also, who thought blog-everyday-month should be November, Thanksgiving at the end really makes it hard to finish strong.  Right now I'm pretty brain dry, hopefully I'll think of something good to finish off the month.  For now I'm just trying to enjoy the long weekend.&lt;br /&gt;&lt;br /&gt;I should probably take a chance to say thanks to Brian Rosner, Michael Trier, and anyone else who wouldn't stop pestering me to get a blog.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-4933265100914673981?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/4933265100914673981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/some-thoughts-on-blogging.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4933265100914673981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/4933265100914673981'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/some-thoughts-on-blogging.html' title='Some thoughts on Blogging'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2487603300330925658</id><published>2008-11-26T00:26:00.002-05:00</published><updated>2008-11-26T00:30:39.471-05:00</updated><title type='text'>Home Sweet Home</title><content type='html'>In the interest of keeping up with post-a-day, I figured at a minimum I'd have a post explaining why I didn't have a real post for today.  I just got home from college today for Thanksgiving, so I've been busy today, sorry :( .&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2487603300330925658?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2487603300330925658/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/home-sweet-home.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2487603300330925658'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2487603300330925658'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/home-sweet-home.html' title='Home Sweet Home'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3534294913305661793</id><published>2008-11-24T18:47:00.005-05:00</published><updated>2008-11-24T19:31:34.900-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>A timeline view in Django</title><content type='html'>One thing a lot of people want to do in Django is to have a timeline view, that shows all the objects of a given set of models ordered by a common key.  Unfortunately the Django ORM doesn't have a way of representing this type of query.  There are a few techniques people use to solve this.  One is to have all of the models inherit from a common baseclass that stores all the common information, and has a method to get the actual object.  The problem with this is that it could execute either O(N) or O(N*k) queries, where N is the number of items and k is the number of models.  It's N if your baseclass has the subtype it is stored on it, in which case you can directly grab it, else it's N*k since you have to try each type.  Another approach is to use a generic relation, this will also need O(N) queries since you need to get the related object for each generic one.  However, there's a better solution.&lt;br /&gt;&lt;br /&gt;What we can do is use get a queryset for each of the models we want to display(O(k) queries), sorted on the correct key, and then use a simple merge to combine all of these querysets into a single list, comparing on a given key.  While this technically may do more operations than the other methods, it does fewer database queries, and this is often the most difficult portion of your application to scale.&lt;br /&gt;&lt;br /&gt;Let's say we have 3 models, new tickets, changesets, and wikipage edits(what you see in a typical Trac install).  We can get our querysets and then merge them like so:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def my_view(request):&lt;br /&gt;   tickets = Ticket.objects.order_by('create_date')&lt;br /&gt;   wikis = WikiEdit.objects.order_by('create_date')&lt;br /&gt;   changesets = Changeset.objects.order_by('create_date')&lt;br /&gt;   objs = merge(tickets, wikis, changesets, field='create_date')&lt;br /&gt;   return render_to_response('my_app/template.html', {'objects': objs})&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we just need to write our merge function:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def merge_lists(left, right, field=None):&lt;br /&gt;    i, j = 0, 0&lt;br /&gt;    result = []&lt;br /&gt;    while (i &lt; len(left) and j &lt; len(right)):&lt;br /&gt;        if getattr(left[i], field) &lt;= getattr(right[j], field):&lt;br /&gt;            result.append(left[i])&lt;br /&gt;            i += 1&lt;br /&gt;        else:&lt;br /&gt;            result.append(right[j])&lt;br /&gt;            j += 1&lt;br /&gt;    result.extend(left[i:])&lt;br /&gt;    result.extend(right[j:])&lt;br /&gt;    return result&lt;br /&gt;&lt;br /&gt;def merge(*querysets, **kwargs):&lt;br /&gt;    field = kwargs.pop('field')&lt;br /&gt;    if field is None:&lt;br /&gt;        raise TypeError('you need to provide a key to do comparisons on')&lt;br /&gt;    if len(querysets) == 1:&lt;br /&gt;        return querysets[0]&lt;br /&gt;    &lt;br /&gt;    qs = [list(x) for x in querysets]&lt;br /&gt;    q1, q2 = qs.pop(), qs.pop()&lt;br /&gt;    result = merge_lists(q1, q2, field)&lt;br /&gt;    for q in qs:&lt;br /&gt;        result = merge_lists(result, q)&lt;br /&gt;    return result&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;There might be a more efficient way to write our merge function, but for now it merges together an arbitrary number of querysets on a given key.&lt;br /&gt;&lt;br /&gt;And that's all their is too it.  If you see a good way to make the merge function more efficient let me know, I would have liked to use Python's included heapq module, but it doesn't have a way to use a custom comparison function that I saw.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3534294913305661793?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3534294913305661793/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/timeline-view-in-django.html#comment-form' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3534294913305661793'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3534294913305661793'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/timeline-view-in-django.html' title='A timeline view in Django'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>12</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1944974568532904946</id><published>2008-11-23T16:52:00.002-05:00</published><updated>2008-11-23T17:04:42.144-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbook'/><category scheme='http://www.blogger.com/atom/ns#' term='laptop'/><category scheme='http://www.blogger.com/atom/ns#' term='eee pc'/><title type='text'>Thinking about netbooks</title><content type='html'>At present I use a fairly powerful laptop as my all in one machine, it's my development environment, it's my gaming machine, and I use it for class.  Before I came to college I used my desktop machine for everything.  However, I'm beginning to think neither of these is the best solution.  My laptop can do everything my desktop used to do, however it isn't as a good at being super portable as a laptop could be, nor does it have the potential to be a powerhouse like a desktop machine can be.  Even though my laptop is a relatively balanced machine with a 15 inch screen I still feel like I'm making compromises on both ends, and I'm wondering if there's a better solution.&lt;br /&gt;&lt;br /&gt;On the laptop side, my laptop isn't as lightweight as one could be, it weighs six or seven pounds.  It also doesn't have as good a battery life as I'd like, 2 to 3 hours.  On the desktop side it's not particularly upgradeable, meaning once it's out of date the whole thing needs to be replaced.  On the other hand it does have some advantages over each, right now it's as powerful as it needs to be for anything I throw at it, and it is mobile enough that I can take it to my classes without having to worry.&lt;br /&gt;&lt;br /&gt;The compromise I'm considering is whether to get a small netbook, such as the Asus EEE PC, for my mobile needs, and use my deskop for the heavy lifting.  This has a few advantages, the EEE PC has a seven hour battery life, and weighs about three pounds.  Plus my desktop runs things fine now, and I can upgrade individual components as needed.  It has some drawbacks though, when I go places I can't bring my gaming machine with me(making going home for the holidays a big pain).&lt;br /&gt;&lt;br /&gt;For now I'm not planning on changing anything about my setup, this is just thinking ahead for when my processing needs eclipse my current laptop(hopefully not another year or two).  Does anyone use a setup like this, or just have a netbook?  What are your thoughts?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1944974568532904946?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1944974568532904946/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/thinking-about-netbooks.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1944974568532904946'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1944974568532904946'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/thinking-about-netbooks.html' title='Thinking about netbooks'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2249727680333972630</id><published>2008-11-23T00:42:00.004-05:00</published><updated>2008-11-23T00:44:32.393-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='al'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><title type='text'>A quick update</title><content type='html'>I've now set up Al to be using GMP for all integers, and I'll be doing the same for floats once they get implemented.  I haven't started benchmarking yet, but it can compile and calculate the factorial of 50000 pretty quickly, and in Python vanilla that would result in a RuntimeError due to a stack overflow, so it's a good starting point.  Sorry for such a short post, I'm pretty tired today.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2249727680333972630?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2249727680333972630/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/quick-update.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2249727680333972630'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2249727680333972630'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/quick-update.html' title='A quick update'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3027922694472230004</id><published>2008-11-21T17:55:00.002-05:00</published><updated>2008-11-21T18:04:23.411-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='c++'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='al'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><title type='text'>My Programming Language - Status Update</title><content type='html'>Over the past few weeks I've been working on compiling my programming language.  At present it works by translating the source into C++, and then you compile that with your compiler of choice.  It's garbage collected, using the excellent Boehm GC library.  At present it can only compile a limited subset of what it can actually parse, or what the interpreter supports.  As of today thought it can compile and run a factorial function, however it can't calculate any factorial greater than 12, due to integer overflow issues.  To solve this I'm either going to use GMP or roll my own Bignum library, and I'm not sure which yet.  On the whole though, progress is good.  The generated C++ is about as good as it could be considering the limitations inherent in turning an interpreted language into a compiled one.  I haven't started benchmarking it yet, that was originally going to be the point of today's post before I ran into the integer overflow issues, however this is an example of the C++ code that is generated.&lt;br /&gt;&lt;br /&gt;Give this Al(also valid Python):&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def fact(n):&lt;br /&gt;   if n == 1 or n == 0:&lt;br /&gt;       return 1&lt;br /&gt;   return n * fact(n-1)&lt;br /&gt;&lt;br /&gt;print(fact(1))&lt;br /&gt;print(fact(12))&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It generated the following C++:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;#include "src/base.h"&lt;br /&gt;&lt;br /&gt;AlObj *fact;&lt;br /&gt;class f0:public AlFunction&lt;br /&gt;{&lt;br /&gt;public:&lt;br /&gt; virtual AlObj * operator () (ARG_TYPE args, KWARG_TYPE kwargs)&lt;br /&gt; {&lt;br /&gt;   AlObj *n = args.back ();&lt;br /&gt;     args.pop_back ();&lt;br /&gt;   if (*&lt;br /&gt; ((*((*(n)) == (AlObj *) (new AlInt (1))))&lt;br /&gt;  || (*(n)) == (AlObj *) (new AlInt (0))))&lt;br /&gt;     {&lt;br /&gt; return (AlObj *) (new AlInt (1));;&lt;br /&gt;     }&lt;br /&gt;   ARG_TYPE t0;&lt;br /&gt;   t0.push_back ((*(n)) - (AlObj *) (new AlInt (1)));&lt;br /&gt;   return (*(n)) * (*fact) (t0, KWARG_TYPE ());&lt;br /&gt; }&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;int&lt;br /&gt;main ()&lt;br /&gt;{&lt;br /&gt; fact = new f0 ();&lt;br /&gt; ARG_TYPE t1;&lt;br /&gt; ARG_TYPE t2;&lt;br /&gt; t2.push_back ((AlObj *) (new AlInt (1)));&lt;br /&gt; t1.push_back ((*fact) (t2, KWARG_TYPE ()));&lt;br /&gt; (*print) (t1, KWARG_TYPE ());&lt;br /&gt; ARG_TYPE t3;&lt;br /&gt; ARG_TYPE t4;&lt;br /&gt; t4.push_back ((AlObj *) (new AlInt (12)));&lt;br /&gt; t3.push_back ((*fact) (t4, KWARG_TYPE ()));&lt;br /&gt; (*print) (t3, KWARG_TYPE ());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;All said and done, I'm pretty impressed!  You can get all the code &lt;a href="http://github.com/alex/alex-s-language/tree/master"&gt;here&lt;/a&gt;, all the compilation work is in the code-generation branch.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3027922694472230004?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3027922694472230004/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/my-programming-language-status-update.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3027922694472230004'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3027922694472230004'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/my-programming-language-status-update.html' title='My Programming Language - Status Update'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-141617535871784129</id><published>2008-11-20T20:44:00.003-05:00</published><updated>2008-11-27T23:58:28.277-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='easy_install'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>Why I don't use easy_install</title><content type='html'>First things first, this post is not meant as a flame, nor should it indicate to you that you shouldn't use it, unless of course you're priorities are perfectly aligned with my own.  That being said, here are the reasons why I don't use easy_install, and how I'd fix them.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;No easy_uninstall.  Zed mentioned this in his PyCon '08 lightning talk, and it's still true.  Yes I can simply remove these files, and yeah I could write a script to do it for me.  But I shouldn't have to, if I can install packages, I should be able to uninstall packages, without doing any work.&lt;/li&gt;&lt;li&gt;I can't update all of my currently installed packages.  For any packages I don't have explicitly installed to a particular version(which to it's credit, easy_install makes very easy to do), it should be very to upgrade all of these, because I probably want to have them up to date, and I can always lock them at a specific version if I want.&lt;/li&gt;&lt;li&gt;I don't want to have two package managers on my machine.  I run Ubuntu, so I already have apt-get, which i find to be a really good system(and doesn't suffer from either of the aforementioned problems).  Having two packages managers inherently brings additional confusion, if a package is available in both which do I install it from?  It's an extra thing to remember to keep up to date(assuming #2 is fixed), and it's, in general, an extra thing to think about, every time I go to update anything on my machine.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So what's my solution?  PyPi is a tremendous resource for Python libraries, and there are great tools in Python for working with it, for example using a setup.py file makes it incredibly easy to get your package up on PyPi, and keep it up to date.  So there's no reason to throw all that stuff out the window.  My solution would be for someone to set up a server that mirrored all the data from PyPi, regularly, and then offered the packages as .deb's(for Debian/Ubuntu users, and as RPMs for Fedora users, etc...).  That way all a user of a given package manager can just add the URL to their sources list, and then install everything that's available from PyPi, plus they derive all of the benefits of their given package manager(for me personally, the ability to uninstall and batch upgrade).&lt;br /&gt;&lt;br /&gt;Note: I'm not suggesting everyone use apt-get, I'm merely suggesting everyone use their native package manager, and there's no reason easy_install/pip/virtualenv can't also be used.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-141617535871784129?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/141617535871784129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/why-i-dont-use-easyinstall.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/141617535871784129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/141617535871784129'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/why-i-dont-use-easyinstall.html' title='Why I don&apos;t use easy_install'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7756258859582896913</id><published>2008-11-19T19:37:00.003-05:00</published><updated>2008-11-19T19:57:20.821-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><category scheme='http://www.blogger.com/atom/ns#' term='turbogears'/><title type='text'>Uncoupled code is good, but doesn't exist</title><content type='html'>Code should try to be as decoupled from the code it depends as possible, I want me C++ to work with any compiler, I want my web framework to work with any ORM, I want my ORM to work with any database.  While all of these are achievable goals, some of the decoupling people are searching for is simply not possible.  At DjangoCon 2008 Mark Ramm made the argument that the Django community was too segregated from the Python community, both in terms of the community itself, and the code, Django for example doesn't take enough advantage of WSGI level middlewear, and has and ORM unto itself.  I believe some of these claims to be true, but I ultiamtely thing the level of uncoupling some people want is simply impossible.&lt;br /&gt;&lt;br /&gt;One of Django's biggest selling features has always been it's automatically generated admin.  The admin requires you to be using Django's models.  Some people would like it to be decoupled.  To them I ask, how?  It's not as if Django's admin has a big if not isinstance(obj, models.Model): raise Exception, it simply expects whatever is passed to it to define the same API as it uses.  And this larger conecern, the Django admin is simply an application, it has no hooks within Django itself, it just happens to live in that namespace, the moment any application does Model.objects.all(), it's no longer ORM agnostic, it's already assumed the usage of a Django ORM.  However, all this means is that applications themselves are inextricably tied to a given ORM, templating language, and any other module they import, you quite simply can't write resonably code that works just as well with two different modules unless they both define the same API.&lt;br /&gt;&lt;br /&gt;Eric Florenzano wrote a great &lt;a href="http://www.eflorenzano.com/blog/post/wsgi-middlware-awesome-django-use-it-more/"&gt;blog post&lt;/a&gt; yesterday about how Django could take better advantage of WSGI middleware, and he's absolutely correct.  It makes no sense for a Django project to have it's own special middlewear for using Python's profiling modules, when it can be done more generic a level up, all the code is in Python afterall.  However, there are also things that you can't abstract out like that, because they require a knowledge of what components you are using, SQLAlchemy has one transation model, Django has another.&lt;br /&gt;&lt;br /&gt;The fact that an application is tied to the modules it uses is not an argument against it.  A Django application is no tightly coupled to Django's ORM and template system is than a Turbo Gears application that uses SQL Alchemy and Mako, which is to say of course they're tied to it, they import those modules, they use them, and unless the other implements the same API you can't just swap them out.  And that's not a bad thing.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7756258859582896913?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7756258859582896913/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/uncoupled-code-is-good-but-doesnt-exist.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7756258859582896913'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7756258859582896913'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/uncoupled-code-is-good-but-doesnt-exist.html' title='Uncoupled code is good, but doesn&apos;t exist'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7884865009758378351</id><published>2008-11-18T13:01:00.003-05:00</published><updated>2008-11-18T13:19:06.118-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='economics'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>What Python learned from economics</title><content type='html'>I find economics to be a fairly interesting subject, mind you I'm bored out of my mind about hearing about the stock markets, derivatives, and whatever else is on CNBC, but I find what guys like Steven Levitt and Steve E. Landsburg do to be fascinating.  A lot of what they write about is why people do what they do, and how to incentivise people to do the right thing.  Yesterday I was reading through David Goodger's &lt;a href="http://python.net/%7Egoodger/projects/pycon/2007/idiomatic/"&gt;Code Like a Pythonista&lt;/a&gt; when I got to this portion:&lt;br /&gt;&lt;blockquote&gt;&lt;p&gt;LUKE: Is &lt;tt class="docutils literal"&gt;&lt;span class="pre"&gt;from&lt;/span&gt; &lt;span class="pre"&gt;module&lt;/span&gt; &lt;span class="pre"&gt;import&lt;/span&gt; &lt;span class="pre"&gt;*&lt;/span&gt;&lt;/tt&gt; better than explicit imports?&lt;/p&gt; &lt;p&gt;YODA: No, not better.  Quicker, easier, more seductive.&lt;/p&gt; &lt;p&gt;LUKE: But how will I know why explicit imports are better than the wild-card form?&lt;/p&gt; &lt;p&gt;YODA: Know you will when your code you try to read six months from now.&lt;/p&gt;&lt;blockquote&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;/p&gt;&lt;/blockquote&gt;And I realized that Python had learned a lot from these economists.&lt;br /&gt;&lt;br /&gt;It's often dificult for a programmer to see the advantage of doing something the right way, which will be benneficial in six months, over just getting something done now.  However, Python enforces doing things the right way, and when doing things the right way is just as easy as doing in the wrong way, you make the intuitive decision of doing things the right way.  Almost every code base I've worked with(outside of Python) had some basic indentation rules that the code observed, Python just encodes this into the language, which requires all code to have a certain level of readability.&lt;br /&gt;&lt;br /&gt;Django has also learned this lesson.  For example, the template language flat out prevents you from putting your business logic inside of it without doing some real work, you don't want to do that work, so you do things the right way and put your business logic in your views.  Another example would be database queries, in Django it would be harder to write a query that injected unescaped into your SQL than it would be do the right thing at use parameterized queries.&lt;br /&gt;&lt;br /&gt;Ultimately, this is why I like Python.  The belief that best practices shouldn't be optional, and that they shouldn't be difficult creates a community where you actively want to go and learn from people's code.  Newcomers to the language aren't encouraged to "just get something working, and then clean it up later," the communiity encourages them to do it right in the first place, and save themselves the time later.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7884865009758378351?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7884865009758378351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-python-learned-from-economics.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7884865009758378351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7884865009758378351'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-python-learned-from-economics.html' title='What Python learned from economics'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-795650228715318931</id><published>2008-11-17T14:44:00.002-05:00</published><updated>2008-11-17T14:56:10.896-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='tests'/><title type='text'>Running the Django Test Suite</title><content type='html'>This question came up in IRC yesterday, so I figured I'd run through it today.  Django has a very extensive test suite that tests all of the components of Django itself.  If you've ever written a patch for Django you probably know that tests are a requirement, for both new features and bug fixes.  I'm going to try to run down how to setup your own testing envrioment.&lt;br /&gt;&lt;br /&gt;First you need to have Django installed somewhere, for now I'll assume you have it in ~/django_src/.  Somewhere on your python path, go ahead are use django-admin.py to create a new project.  I've named this project django_test.  Next, inside of that project create a folder named settings, and move settings.py into that folder and renmae it __init__.py.  The reason we're going to have a settings directory is so that we can have subsettings for individual scenarios.  Put some default settings for the various field in there now, for example my default settings provides the necessary options for SQLite.  Now if there are any other subsettings you wish to setup create a file for them in the settings directory, and at the top of this file put from django_test.settings import *, followed by whatever settings you wish to overide.  For example I have a mysql.py that overides my default SQLite database settings with MySQL values.&lt;br /&gt;&lt;br /&gt;Now that we have our test settings, go to the directory where you have Django installed.  To run the tests do ./tests/runtests.py --settings=django_test.settings.  You can also use the DJANGO_SETTINGS_MODULE enviromental variable in place of passing in the settings like this.  You can also provide a verbosity argument, the default is 0, I prefer to run with verbosity=1 because this keeps you up to date on the progress of the tests, without too much extraneus output.&lt;br /&gt;&lt;br /&gt;Usually when you are working on a patch you don't need to run the entire test suite, your patch only affects a few tests.  Therefore, you can provide a list of tests you'd like to run, so for example you could do ./tests/runtests.py --settings=django_test.settings.mysql -v 1 model_forms model_formsets.  This will run the model_forms and model_formsets tests, with your mysql settings, at verbosity level 1.&lt;br /&gt;&lt;br /&gt;And that's all it takes to run the Django test suite.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-795650228715318931?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/795650228715318931/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/running-django-test-suite.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/795650228715318931'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/795650228715318931'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/running-django-test-suite.html' title='Running the Django Test Suite'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-1853953689814635961</id><published>2008-11-16T19:03:00.004-05:00</published><updated>2008-11-16T20:38:16.186-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><title type='text'>What I'm excited about in Django 1.1</title><content type='html'>This past week, Jacob Kaplan-Moss put together the list of all of the features proposed for Django 1.1, and began to solicit comments on them.  This is going to be a list of features I'm excited about.&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Making admin URLs reversable.  Currently the Django Admin uses a bit of a convulted scheme to route URLs.  The proposal is to have them work using the current URL scheme.  This is something I've been working on for a while, and hoping to see it to completion.&lt;/li&gt;&lt;li&gt;Comment-utils inclusion.  The proposal is to include the moderation level features from comment-utils in Django.  I think this is a great idea, and can't wait to see what sort of spam check schemes people implement once moderation facilities are included.&lt;/li&gt;&lt;li&gt;Message passing for anonymous users.  This is basically session level message passing.  This is something I've had to implement in past, so I'm looking forward to this.&lt;/li&gt;&lt;li&gt;ORM aggregation, as part of the Google Summer of Code Nicolas Lara, mentored by Russell Keith-Magee, implemented this.  I love the API design, and it's a hugely requested feature, I can't wait to point new users to the docs, rather than explaining to them that it's coming soon.&lt;/li&gt;&lt;li&gt;ORM expression support, this work was also done by Nicolas Lara, and will let you do things like Model.objects.filter(height__gt=F('width')), or Model.objects.update(salary = F('salary')*1.2).&lt;/li&gt;&lt;li&gt;Model Validation, before 1.0 I implemented unique, and unique_together checks for model forms.  That's pretty much a massive special case of this.&lt;/li&gt;&lt;li&gt;Class-based generic views, often one of the first things a new user to Django will learn to do is create a view that simply wraps a generic view, in order to do some custom filtering on a queryset.  This is a great solution for that usecase, however as users want to inject more and more flexibility into a generic view it can lead to a huge number of settings.  Rather than this, subclassing a generic view could provide a nice clean solution.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;These will all be great features, and there are many more proposed(you can see them all &lt;a href="http://spreadsheets.google.com/ccc?key=pSqnCvef6OXmGWQ9qbEVMeA"&gt;here&lt;/a&gt;), however these features only happen because people write code for them.  If there's a feature you're excited about, or interested in making a reality, try to contribute to it, even if it's just writing some unit tests.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-1853953689814635961?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/1853953689814635961/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-im-excited-about-in-django-11.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1853953689814635961'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/1853953689814635961'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-im-excited-about-in-django-11.html' title='What I&apos;m excited about in Django 1.1'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3037158214245032326</id><published>2008-11-15T17:26:00.004-05:00</published><updated>2008-11-15T21:59:31.162-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tips'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>Python Things</title><content type='html'>I wasn't really sure what to name today's post, but it's basically going to be nifty things you can do in Python, and general tips.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;SystemExit&lt;/span&gt;, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;sys&lt;/span&gt;.exit() raises &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;SystemExit&lt;/span&gt;, if you actually want to keep going, you can just catch this exception, nothing special about it.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;iter&lt;/span&gt;(callable, terminal), basically if you use &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;iter&lt;/span&gt; in this way, it will keep calling the callable until the callable returns terminal, than it beaks.&lt;/li&gt;&lt;li&gt;a &amp;lt; x &amp;lt; b , in Python you can chain comparison operators like this.  That's the same as writing a &amp;lt; x and x &amp;lt; b.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;dict(), amongst the other ways to instantiate a dictionary in Python, you can give it a list of two &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;tuples&lt;/span&gt;, so for example, [('a', 2), ('b', 3')] becomes {'a': 2, 'b': 3}.&lt;/li&gt;&lt;li&gt;open(&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;filename&lt;/span&gt;), is an &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;iterable&lt;/span&gt;, each iteration yields another line.&lt;/li&gt;&lt;li&gt;If you don't need ordering, use set() instead of list().  set() has better &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;runtime&lt;/span&gt; for just about every operation, so if you don't need the ordering, use it.&lt;/li&gt;&lt;li&gt;Python comes with turtle graphics.  This probably doesn't matter to most people, but if you want to help get a kid into programming, import turtle can be a great way.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;pdb&lt;/span&gt;, the Python debugger is simply invaluable, try: code that isn't working, except &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;ExceptionThatGetsRaised&lt;/span&gt;: import &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;pdb&lt;/span&gt;; &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;pdb&lt;/span&gt;.set_trace() is all it takes to get started with the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_13"&gt;itneractive&lt;/span&gt; debugger.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_14"&gt;webbrowser&lt;/span&gt;.open(&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15"&gt;url&lt;/span&gt;), this module is just cool, it opens up the users browser to the desired URL.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;And those are my tips!  Please share yours.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3037158214245032326?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3037158214245032326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/python-things.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3037158214245032326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3037158214245032326'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/python-things.html' title='Python Things'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-171676479372191301</id><published>2008-11-14T18:31:00.003-05:00</published><updated>2008-11-14T18:43:44.657-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internals'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='disclaimer'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>And now for the disclaimer</title><content type='html'>I've been discussing portions of how the Django internals work, and this is powerful knowledge for a Django user.  However, it's also internals, and unless they are documented internals are not guaranteed to continue to work.  That doesn't mean they break very frequently, they don't, but you should be aware that it has no guarantee of compatibility going forward.&lt;br /&gt;&lt;br /&gt;Having said that, I've already discussed ways you can use this to do powerful things by using these, and you've probably seen other ways to use these in your own code.  In my development I don't really balk at the idea of using the internals, because I track Django's development very aggressively, and I can update my code as necessary, but for a lot of developers that isn't really an options.  Once you deploy something you need it to work, so your options are to either lock your code at a specific version of Django, or not using these internals.  What happens if you want to update to Django 1.1 for aggregation support, but 1.1 also removed some internal helper function you were using.  Something similar to this happened to django-tagging, before the queryset-refactor branch was merged into trunk there was a helper function to parse portions of the query, and django-tagging made use of this.  However, queryset-refactor obsoleted this function and removed, and so django-tagging had to update in order to work going forward, needing to either handle this situation in the code itself, or to maintain two separate branches.&lt;br /&gt;&lt;br /&gt;In my opinion, while these things may break, they are worth using if you need them, because they let you do very powerful things.  This may not be the answer for everyone though.  In any event I'm going to continue writing about them, and if they interest you Marty Alchin has a book coming out, named &lt;a href="http://prodjango.com/"&gt;Pro Django&lt;/a&gt;, that looks like it will cover a lot of these.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-171676479372191301?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/171676479372191301/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/and-now-for-disclaimer.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/171676479372191301'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/171676479372191301'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/and-now-for-disclaimer.html' title='And now for the disclaimer'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-6262596043375532118</id><published>2008-11-13T17:49:00.004-05:00</published><updated>2008-11-13T18:21:16.179-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='foreignkey'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><category scheme='http://www.blogger.com/atom/ns#' term='metaclass'/><title type='text'>Django Models - Digging a Little Deeper</title><content type='html'>For those of you who read my last post on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Django&lt;/span&gt; models you probably noticed that I skirted over a few details, specifically for quite a few items I said we, "added them to the new class".  But what exactly does that entail?  Here I'm going to look at the add_to_class method that's present on the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;ModelBase&lt;/span&gt; &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;metaclass&lt;/span&gt; we look at earlier, and the contribute_to_class method that's present on a number of classes throughout &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Django&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;So first, the add_to_class method.  This is called for each item we add to the new class, and what it does is if that has a contribute_to_class method than we call that with the new class, and it's name(the name it should attach itself to the new class as) as arguments.  Otherwise we simply set that attribute to that value on the new class.  So for example new_class.add_to_class('&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;abc&lt;/span&gt;', 3), 3 doesn't have a contribute_to_class method, so we just do &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_5"&gt;setattr&lt;/span&gt;(new_class, '&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;abc&lt;/span&gt;', 3).&lt;br /&gt;&lt;br /&gt;The contribute_to_class method is more common for things you set on your class, like Fields or Managers.  The contribute_to_class method on these objects is responsible for doing whatever is necessary to add it to the new class and do it's setup.  If you remember from my first blog post about User Foreign Keys, we used the contribute_to_class method to add a new manager to our class.  Here we're going to look at what a few of the &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;builtin&lt;/span&gt; contribute_to_class methods do.&lt;br /&gt;&lt;br /&gt;The first case is a manager.  The manager sets it's model attribute to be the model it's added to.  Then it checks to see whether or not the model already has an _default_manager attribute, if it doesn't, or if it's creation counter is lower than that of the current creation counter, it sets itself as the default manager on the new class.  The creation counter is essentially a way for &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;Django&lt;/span&gt; to keep track of which manager was added to the model first.  Lastly, if this is an abstract model, it adds itself to the abstract_managers list in _meta on the model.&lt;br /&gt;&lt;br /&gt;The next case is if the object is a field, different fields actually do slightly different things, but first we'll cover the general field case.  It also, first, sets a few of it's internal attributes, to know what it's name is on the new model, additionally calculating it's column name in the db, and it's verbose_name if one isn't explicitly provided.  Next it calls add_field on _meta of the model to add itself to _meta.  Lastly, if the field has choices, it sets the get_FIELD_display method on the class.&lt;br /&gt;&lt;br /&gt;Another case is for file fields.  They do everything a normal field does, plus some more stuff.  They also add a &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_9"&gt;FileDescriptor&lt;/span&gt; to the new class, and they also add a signal receiver so that when an instance of the model is deleted the file also gets deleted.&lt;br /&gt;&lt;br /&gt;The final case is for related fields.  This is also the most complicated case.  I won't describe exactly what this code does, but it's biggest responsibility is to set up the reverse descriptors on the related model, those are nice things that let you author_obj.books.all().&lt;br /&gt;&lt;br /&gt;Hopefully this gives you a good idea of what to do if you wanted to create a new field like object in &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;Django&lt;/span&gt;.  For another example of using these techniques, take a look at the generic foreign key field in django.contrib.contenttypes, &lt;a href="http://code.djangoproject.com/browser/django/trunk/django/contrib/contenttypes/generic.py#L16"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-6262596043375532118?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/6262596043375532118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/django-models-digging-little-deeper.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6262596043375532118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/6262596043375532118'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/django-models-digging-little-deeper.html' title='Django Models - Digging a Little Deeper'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8039303374046305247</id><published>2008-11-12T13:25:00.003-05:00</published><updated>2008-11-12T13:42:39.455-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='ubuntu'/><title type='text'>What software do I use?</title><content type='html'>Taking a page from &lt;a href="http://oebfare.com"&gt;Brain &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_0"&gt;Rosner&lt;/span&gt;&lt;/a&gt;'s book, today I'm going to overview the software I use day to day.  I'm only going to cover stuff I use under &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_1"&gt;Ubuntu&lt;/span&gt;, I keep Windows &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_2"&gt;XP&lt;/span&gt; on my system for gaming, but I'm not going to cover it here.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_3"&gt;Ubuntu&lt;/span&gt;, I've been using the current version, Intrepid Ibex, since Alpha 4, and I love it.  You quite simply couldn't get me to go back to Windows.&lt;/li&gt;&lt;li&gt;Python, it's my go to language, I fell in love about 14 months ago and I'm never going to leave it.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_4"&gt;Django&lt;/span&gt;, it's my framework of choice, it's simple, clean, and well designed.&lt;/li&gt;&lt;li&gt;g++, C++ is the language used in my CS class, so I use my favorite free compiler.&lt;/li&gt;&lt;li&gt;gnome-do, this is an incredibly handy &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_5"&gt;application&lt;/span&gt;, similar to Quicksilver for OS X, it makes simple things super fast, stuff like spawning the terminal, posting a tweet, searching for a file, or calling on &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_6"&gt;Google's&lt;/span&gt; awesome calculator.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_7"&gt;Firefox&lt;/span&gt;, the tried and true free browser, I also have to thank, Gmail Notifier, Firebug, Download them All, and Reload Every.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_8"&gt;Chatzilla&lt;/span&gt;, I figured this &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_9"&gt;extension&lt;/span&gt; deserved it's own mention, I use it almost 24/7 and couldn't live without it.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_10"&gt;Gedit&lt;/span&gt;, who would think that the text editor that came with my OS would be so great?&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_11"&gt;VLC&lt;/span&gt; and Totem, you guys are both great, &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_12"&gt;VLC&lt;/span&gt; is a bit &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_13"&gt;nicer&lt;/span&gt; for playing &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_14"&gt;flvs&lt;/span&gt;, but I love Totem's ability to search and play movies from &lt;span class="blsp-spelling-error" id="SPELLING_ERROR_15"&gt;Youtube&lt;/span&gt;.&lt;/li&gt;&lt;li&gt;&lt;span class="blsp-spelling-error" id="SPELLING_ERROR_16"&gt;Skype&lt;/span&gt;, makes it easy to get &lt;span class="blsp-spelling-corrected" id="SPELLING_ERROR_17"&gt;conference&lt;/span&gt; calls going with 5 friends, couldn't live without it.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;As you can see most of the software I use is open source.  I don't imagine anything I use is very outside the mainstream, but all of these projections deserve a round of applause for being great.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8039303374046305247?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8039303374046305247/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-software-do-i-use.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8039303374046305247'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8039303374046305247'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/what-software-do-i-use.html' title='What software do I use?'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8580800966571698166</id><published>2008-11-10T22:16:00.003-05:00</published><updated>2008-11-11T00:02:25.912-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><category scheme='http://www.blogger.com/atom/ns#' term='metaclass'/><title type='text'>How the Heck do Django Models Work</title><content type='html'>Anyone who has used Django for just about any length of time has probably used a Django model, and possibly wondered how it works.  The key to the whole thing is what's known as a metaclass, a metaclass is essentially a class that defines how a class is created.  All the code for this occurs is &lt;a href="http://code.djangoproject.com/browser/django/trunk/django/db/models/base.py#L25"&gt;here&lt;/a&gt;.  And without further ado, let's see what this does.&lt;br /&gt;&lt;br /&gt;So the first thing to look at is the method, __new__,  __new__ is sort of like __init__, except instead of returning an instance of the class, it returns a new class.  You can sort of see this is the argument signature, it takes cls, name, bases, and attrs.  Where __init__ takes self, __new__ takes cls.  Name is a string which is the name of the class, bases is the class that this new class is a subclass of, and attrs is a dictionary mapping names to class attributes.&lt;br /&gt;&lt;br /&gt;The first thing the __new__ method does is check if the new class is a subclass of ModelBase, and if it's not, it bails out, and returns a normal class.  The next thing is it gets the module of the class, and sets the attribute on the new class(this is going to be a recurring theme, getting something from the original class, and putting it in the right place on the new class).  Then it checks if it has a Meta class(where you define your Model level options), it has to look in two places for this, first in the attrs dictionary, this is where it will be if you stick your class Meta inside your class.  However, because of inheritance, we also have to check if the class has an _meta attribute already(this is where Django ultimately stores a bunch of internal information), and handle that scenario as well.&lt;br /&gt;&lt;br /&gt;Next we get the app_label attribute, for this we either use the app_label attribute in the Meta class, or we pull it out of sys.modules.  Lastly(at least for Meta), we build an instance of the Options class(which lives at django.db.models.options.Option) and add it to the new class as _meta.  Next, if this class isn't an abstract base class we add the DoesNotExist and MultipleObjectsReturned exceptions to the class, and also inherit the ordering and get_latest_by attributes if we are a subclass.&lt;br /&gt;&lt;br /&gt;Now we start getting to adding fields and stuff.  First we check if we have an _default_manager attribute, and if not, we set it to None.  Next we check if we've already defined the class, and if we have, we just return the class we already created.  Now we go through each item that's left in the attrs dictionary and class the add_to_class method with it on the new class.  add_to_class is a piece of internals that you may recognize from my first two blog posts, and what exactly it does I'll explain exactly what it does in another most, but at it's most basic level it adds each item in the dictionary to the new class, and each item knows where exactly it needs to get put.&lt;br /&gt;&lt;br /&gt;Now we do a bunch of stuff to deal with inherited models.  We iterate through ever item in bases, that's also a subclass of models.Model, and do the following: if it doesn't have an _meta attribute, we ignore it.  If the parent isn't an abstract base class, if we already have a OneToOne field to it we set it up as a primary key, otherwise we create a new OneToOne field and install it as a primary key for the model.  And now, if it is an abstract class, we iterate through the fields, if any of these fields has a name that is already defined on our class, we raise an error, otherwise we add that field to our class.  And now we move managers from the parents down to the new class.  Essentially we juts copy them over, and we also copy over virtual fields(these are things like GenericForeignKeys, which doesn't actually have a database field, but we still need to pass down and setup appropriately).&lt;br /&gt;&lt;br /&gt;And then we do a few final pieces of cleanup.  We make sure our new class doesn't have abstract=True in it's _meta, even if it's inherited from an abstract class.  We add a few methods(get_next_in_order, and other), we inherit the docstring, or set a new one, and we send the class prepared signal.  Finally, we register the model with Django's model loading system, and return the instance in Django's model cache, this is to make sure we don't have duplicate copies of the class floating around.&lt;br /&gt;&lt;br /&gt;And that's it!  Obviously I've skirted over how exactly somethings occur, but you should have a basic idea of what occurs.  As always with Django, the source is an excellent resource.  Hopefully you have a better idea of what exactly happens when you subclass models.Model now.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8580800966571698166?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8580800966571698166/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/how-heck-do-django-models-work.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8580800966571698166'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8580800966571698166'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/how-heck-do-django-models-work.html' title='How the Heck do Django Models Work'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-8845074556316538507</id><published>2008-11-10T00:10:00.002-05:00</published><updated>2008-11-10T00:27:46.790-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='yacc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='lex'/><category scheme='http://www.blogger.com/atom/ns#' term='ply'/><title type='text'>Getting Started With PLY - Part 3</title><content type='html'>As promised, today we'll be looking at implementing additional arithmetic operations, dealing with order of operations, and adding variables to our languages, so without further ado, let's jump into the code.&lt;br /&gt;&lt;br /&gt;We can replace our old addition rule with this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;import operator&lt;br /&gt;def p_expression_arithmetic(p):&lt;br /&gt;    '''&lt;br /&gt;    expression : expression PLUS expression&lt;br /&gt;               | expression MINUS expression&lt;br /&gt;               | expression TIMES expression&lt;br /&gt;               | expression DIVIDE expression&lt;br /&gt;    '''&lt;br /&gt;    OPS = {&lt;br /&gt;        '+': operator.add,&lt;br /&gt;        '-': operator.sub,&lt;br /&gt;        '*': operator.mul,&lt;br /&gt;        '/': operator.div&lt;br /&gt;    }&lt;br /&gt;    p[0] = OPS[p[2]](p[1], p[3])&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Hopefully what this code does is pretty clear, the | operator in the rule is an or option.  So if we match any of these, we get the correct function out of our ops dictionary(if you aren't familiar with operator module check it out, it's awesome), and then call it with the two arguments.&lt;br /&gt;&lt;br /&gt;This handles the arithmetic correctly, but doesn't handle order of operations, so lets add that in:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;precedence = (&lt;br /&gt;    ('left', 'PLUS', 'MINUS'),&lt;br /&gt;    ('left', 'TIMES', 'DIVIDE'),&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What this says is all these operations are left-associative, and TIMES and DIVIDE have a high precedence than PLUS and MINUS(both groupings have equal precedence, and thus read left to right).&lt;br /&gt;&lt;br /&gt;Now that we have a fully functioning calculator, let's add in variables, first we need to add a token for NAMES(variables) and for the assignment operator:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def t_NAME(t):&lt;br /&gt;    r'[a-zA-Z_][a-zA-Z_0-9]*'&lt;br /&gt;    return t&lt;br /&gt;&lt;br /&gt;t_EQ = r'='&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And of course add NAME and EQ to the list of tokens, and now a few parsing rules:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;names = {}&lt;br /&gt;&lt;br /&gt;def p_expression_name(p):&lt;br /&gt;    '''&lt;br /&gt;    expression : NAME&lt;br /&gt;    '''&lt;br /&gt;    p[0] = names[p[1]]&lt;br /&gt;&lt;br /&gt;def p_assignment(p):&lt;br /&gt;    '''&lt;br /&gt;    assignment : NAME EQ expression&lt;br /&gt;    '''&lt;br /&gt;    names[p[1]] = p[3]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So here we define a names dictionary, it will map variables to values.  Hopefully the parse rules are fairly obvious, and everything makes sense.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-8845074556316538507?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/8845074556316538507/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/getting-started-with-ply-part-3.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8845074556316538507'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/8845074556316538507'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/getting-started-with-ply-part-3.html' title='Getting Started With PLY - Part 3'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5570359501857482823</id><published>2008-11-09T01:19:00.004-05:00</published><updated>2008-11-09T01:33:35.103-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='yacc'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='ply'/><title type='text'>Getting Started With PLY - Part 2</title><content type='html'>Yesterday we created are tokens, and using these we can parse our language(which right now is a calculator) into some tokens.  Unfortunately this isn't very useful.  So today we are going to start writing a grammar, and building an interpreter around it.&lt;br /&gt;&lt;br /&gt;In PLY, grammar rules are defined similarly to tokens, that is, using docstrings.  Here's what a few grammar rules for out language might look like:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;def p_expression_plus(p):&lt;br /&gt;   '''&lt;br /&gt;   expression : expression PLUS expression&lt;br /&gt;   '''&lt;br /&gt;   p[0] = p[1] + t[3]&lt;br /&gt;&lt;br /&gt;def p_expression_number(p):&lt;br /&gt;   '''&lt;br /&gt;   expression : NUMBER&lt;br /&gt;   '''&lt;br /&gt;   p[0] = [1]&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So the first docstring works is, an expression is defined as expression PLUS expression.  Here PLUS is the token we defined earlier, and expression is any other way we've defined expression, so an expression is also a number(which is the token we defined earlier).  The way the code works is essentially that p[0] is the result, and each piece of the definition is it's own subscript, so p[1] and p[3] refer to the two expression in the plus expression we defined.&lt;br /&gt;&lt;br /&gt;To actually use this parser we've defined we do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;parser = yacc.yacc()&lt;br /&gt;if __name__ == '__main__':&lt;br /&gt;    while True:&lt;br /&gt;        try:&lt;br /&gt;            s = raw_input('calc &gt; ')&lt;br /&gt;        except EOFError:&lt;br /&gt;            break&lt;br /&gt;        if not s:&lt;br /&gt;            continue&lt;br /&gt;        result = parser.parse(s)&lt;br /&gt;        print result&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Try it out!  As an exercise, the reader can implement other operations(remember the order of operations!), and perhaps variable.  Tomorrow, I'll be discussing implementing these.  As always, the PLY documentation is excellent, and available &lt;a href="http://www.dabeaz.com/ply/ply.html"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5570359501857482823?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5570359501857482823/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/getting-started-with-ply-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5570359501857482823'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5570359501857482823'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/getting-started-with-ply-part-2.html' title='Getting Started With PLY - Part 2'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-3765727941484183408</id><published>2008-11-08T00:51:00.003-05:00</published><updated>2008-11-08T01:08:02.345-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='lex'/><category scheme='http://www.blogger.com/atom/ns#' term='ply'/><title type='text'>Getting Started With PLY</title><content type='html'>The other day I mentioned I was using PLY in my post about building a language, so today I'm going to describe getting started with PLY, specifically the tokenization phase.  For those who don't know much about parsing a language, the tokenization phase is where we take the source file, and turn it into a series of tokens.  For example, turning a = 3 + 4 into, NAME EQUALS 3 PLUS 4.  As you can see that simple assignment becomes 5 tokens, each number is a token, both numbers are tokens, and a is a NAME token.  So how do we do this in PLY?&lt;br /&gt;&lt;br /&gt;PLY's method for defining tokenization rules is very creative.  First you define a list of tokens, for example:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;tokens = (&lt;br /&gt;   'NUMBER',&lt;br /&gt;   'PLUS',&lt;br /&gt;   'MINUS',&lt;br /&gt;   'TIMES',&lt;br /&gt;   'DIVIDE',&lt;br /&gt;)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here we have defined the types of tokens we will define, what each of these is should be self explanatory.  Then we define some rules, they look like this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;t_PLUS    = r'\+'&lt;br /&gt;t_MINUS   = r'-'&lt;br /&gt;t_TIMES   = r'\*'&lt;br /&gt;t_DIVIDE  = r'/'&lt;br /&gt;def t_NUMBER(t):&lt;br /&gt;    r'\d+'&lt;br /&gt;    try:&lt;br /&gt;         t.value = int(t.value)    &lt;br /&gt;    except ValueError:&lt;br /&gt;  t.value = 0&lt;br /&gt;    return t&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This is probably less obvious.  There are 2 ways to define the rules for a token, either as a string, or as a function.  Either way they are named t_TOKEN_NAME.  For a lot of tokens you can just do the string, those are the ones that don't require processing, and the string is just a regex that matches the token.  For things that do need processing, we can define a function.  The function takes 1 parameter, which is a lexer objecct, as you can see in our example, we take in t, and since we are defining a number token we set the value to be the integer for the string representation from the source code.  The interesting thing here is how we define the rule for, PLY uses the docstring for a function to get the regex for it.&lt;br /&gt;&lt;br /&gt;Now that we have all of our rules set up we need to actually build the lexer object:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;lexer = lex.lex()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And then we can use the input() function on a lexer to provide the source code, and the token function to pop the next token off the lexer.&lt;br /&gt;&lt;br /&gt;That's all for today, in the future we'll take a look at the other components of building the grammar of a language, and at how we implement it.  For more information now, PLY has excellent documentation, available &lt;a href="http://www.dabeaz.com/ply/ply.html"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-3765727941484183408?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/3765727941484183408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/getting-started-with-ply.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3765727941484183408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/3765727941484183408'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/getting-started-with-ply.html' title='Getting Started With PLY'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-674664094930247361</id><published>2008-11-07T13:26:00.003-05:00</published><updated>2008-11-07T13:35:26.355-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='PHP'/><category scheme='http://www.blogger.com/atom/ns#' term='obama'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><title type='text'>That's not change we can believe in</title><content type='html'>Yesterday president-elect Obama's campaign unveiled their transitional website, &lt;a href="http://www.change.gov/"&gt;change.gov&lt;/a&gt;.  So, as someone who's interested in these things I immediately began to look at what language, framework, or software package they were using.  The first thing I saw was that they were using Apache, however beyond that there were no distinctive headers.  None of the pages had tell-tale extensions like .php or .aspx.  However, one thing that struck me was that most pages were at a url in the form of /page/*/, which is the same format of the Obama campaign website, which I knew was powered by Blue State Digital's CMS.  On the Obama campaign's site however, there were a few pages with those tell-tale .php extension, so I've come to the conclusion that the new site also uses PHP.  And to that I say, that's not change we can believe in.&lt;br /&gt;&lt;br /&gt;PHP has been something of a powerhouse in web development for the last few years, noted for it's ease of deployment and quick startup times, it's drawn in legions of new users.  However, PHP has several notable flaws.  Firstly, it doesn't encourage best practices, ranging from things like code organization(PHP currently has no concept of namespaces), to database security(the included mysql database adapter doesn't feature parameterized queries), and beyond.  However, this isn't just another post to bash on PHP(as much as I'd like to do one), there are already plenty of those out there.  This post is instead to offer some of the benefits of switching, to Python, or Ruby, or whatever else.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;You develop faster.  Using a framework like Django, or Rails, or TurboGears let's you do things very quickly.&lt;/li&gt;&lt;li&gt;You get the benefits of the community, with Django you get all the reusable applications, Rails has plugins, TurboGears has middleware.  Things like these quite simply don't exist in the PHP world.&lt;/li&gt;&lt;li&gt;You get a philosophy.  As far as I can tell, PHP has no philosophy, however both Python and Ruby do, and so do their respective frameworks.  Working within a consistant philsophy makes development remarkably more sane.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;If you currently are a user of PHP, I beg of you,  take a chance, try out Ruby or Python, or whatever else.  Give Django, or TurboGears, or Rails a shot.  Even if you don't end up liking it, or switching, it's worth giving it a shot.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-674664094930247361?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/674664094930247361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/thats-not-change-we-can-believe-in.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/674664094930247361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/674664094930247361'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/thats-not-change-we-can-believe-in.html' title='That&apos;s not change we can believe in'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-2730035820045904297</id><published>2008-11-06T20:24:00.001-05:00</published><updated>2008-11-06T20:26:56.397-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='ply'/><category scheme='http://www.blogger.com/atom/ns#' term='compile'/><title type='text'>Building a Programming Language with Python</title><content type='html'>One of my side projects of late has been building a programming language in Python, using the PLY library.  PLY is essentially a Python implementation of the classic Lex and Yacc tools.  The language, at present, has a syntax almost exactly the same as Python's(the notable difference, in so far as features that have been implemented, is that you are not allowed to have multiple statements on the same line, or to put anything following a colon on the same line).  The language(currently called 'Al' although that's more of a working name), is a dynamic language that builds up an syntax tree for the code, and than executes it.  However, the long term goal is to have it actually be a compiled language, similar to Lisp or C.  Essentially the mechanism for doing this will be the same as how a C++ compiler handles multiple dispatch, which is dynamically at run time.&lt;br /&gt;&lt;br /&gt;At present however, this mythical fully compiled language is far from complete, I haven't even began to think about the assembly generation, mostly because I don't know assembly at all, and one of the courses I will be taking next semester is one which covers assembly code.  However, the question that has to be asked, are what are the advantages of a compiled language, and what are the costs?&lt;br /&gt;&lt;br /&gt;First the benefits:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt; It's faster, even a worst case C++ program that fully utilizes multiple dispatch at runtime will go faster than a program using the same algorithms in Python.&lt;/li&gt;&lt;li&gt;You get an executable at the end.  This is a huge advantage for distribution, you don't need to distribute the source code, and you have an exe to give to people.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;There are probably others, but I'm assuming the semantics of a language similar to Python, so I haven't included things like compile time type checking.  And now the disadvantages:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;You lose some of the dynamicism.  Doing things like eval(), or dynamic imports is inherently harder, if not impossible.&lt;/li&gt;&lt;li&gt;You lose the REPL(interactive interpreter).&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;So can we overcome those?  As far as I can tell the first should be doable, eval() necessitates the inclusion of an interpreter with the language, the thought of this already has to be making people think this is just going to end up as a VM.  But, I think this can be overcome, we can know, at compile time, whether or not a user will be using eval, and decide then whether or not to compile the interpreter and link against it.  Dynamic imports are, if anything harder, I think, I think this is just an issue of doing run time linking, but I'm not sure.  As for the issue of the REPL, this is a non-issue as far as I'm concerned, there is no inherent reason a compiled language can't have a REPL, we just often don't, languages like Common Lisp have long had both.&lt;br /&gt;&lt;br /&gt;So now, let's see some code.  I hope to have some code to show off, that handles at least a subset of Python, for PyCon 2009, as work begins on assembly generation I will post here.  For anyone interested in the code at present, you can see it &lt;a href="http://github.com/alex/alex-s-language/tree/master"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-2730035820045904297?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/2730035820045904297/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/building-programming-language-with.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2730035820045904297'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/2730035820045904297'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/building-programming-language-with.html' title='Building a Programming Language with Python'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5963265740261972510</id><published>2008-11-05T16:57:00.001-05:00</published><updated>2008-11-05T18:40:24.229-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='multiprocess'/><category scheme='http://www.blogger.com/atom/ns#' term='python'/><category scheme='http://www.blogger.com/atom/ns#' term='pygtk'/><title type='text'>PyGTK and Multiprocessing</title><content type='html'>Yesterday was election day, and for many people that meant long nights following the results, waiting to see who would be declared the next president of the United States of America.  Politics is a game of numbers, and it's nice to offload the crunching to our computers.  I had written up a simple application for projecting win likelihood for the candidates based on the likelihood of a win in an individual state.  If you are interested in the application itself you can see it &lt;a href="http://github.com/alex/election-sim/tree/master"&gt;here&lt;/a&gt;.  However this post is going to look at the new multiprocessing library, and how I used it with PyGTK.&lt;br /&gt;&lt;br /&gt;Part of my application is that whenever you update a probability for a given candidate in a given state it recomputes their win percentage for the election as a whole.  To make this as accurate as possible it runs multiple simulations of the scenario to compute the win percentage.  Originally I was running these computations in the same thread as the GUI work and I found that I could only do about 250 simulations before it had a drastically negative impact on usability.  So the next step was to offload these calculations into another process.&lt;br /&gt;&lt;br /&gt;To go about this I created an Updater class which is a subclass of multiprocessing.Process.  It takes a pipe as it's only argument, and it's run method just loops forever polling the pipe for new projection results, tabulating them, and then sending the projection back through the pipe.&lt;br /&gt;&lt;br /&gt;In the main process the application obviously starts by creating a duplex pipe, spawning the second process(and giving it the pipe).  Then, using the facilities of the gobject library, it sets up a method that checks for new projection results and updates the GUI to be executed whenever the main thread is idle(gobject.idle_add).  And lastly the signal responder that gets called whenever the user changes some data simply marshals up the necessary data, and sets it through the pipe to the other process.&lt;br /&gt;&lt;br /&gt;And that's all, in total I believe it was under 25 lines of code changed to make my application use a separate process for calculation.&lt;br /&gt;&lt;br /&gt;EDIT: Upon request, &lt;a href="http://github.com/alex/election-sim/commit/cc93df649d1295deab96f1f1ab1ff709e8b0f391"&gt;this&lt;/a&gt; is the diff where I made the original changes, several subsequent commits will better reflect what is described here though.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5963265740261972510?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5963265740261972510/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/pygtk-and-multiprocessing.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5963265740261972510'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5963265740261972510'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/pygtk-and-multiprocessing.html' title='PyGTK and Multiprocessing'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-5484634966753561934</id><published>2008-11-04T13:08:00.001-05:00</published><updated>2008-11-10T20:37:08.080-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='foreignkey'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>More Laziness with Foreign Keys</title><content type='html'>Yesterday we looked at building a form field to make the process of getting a ForeignKey to the User model more simple, and to provide us with some useful tools, like the manager.  But this process can be generalized, and made more robust.  First we want to have a lazy ForeignKey field for all models(be careful not to confuse the term lazy, here I use it to refer to the fact that I am a lazy person, not the fact that foreign keys are lazy loaded).&lt;br /&gt;&lt;br /&gt;A more generic lazy foreign key field might look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    from django.db.models import ForeignKey, Manager&lt;br /&gt;&lt;br /&gt;    class LazyForeignKey(ForeignKey):&lt;br /&gt;        def __init__(self, *args, **kwargs):&lt;br /&gt;            model = kwargs.get('to')&lt;br /&gt;            if model_name is None:&lt;br /&gt;                model = args[0]&lt;br /&gt;            try:&lt;br /&gt;                name = model._meta.object_name.lower()&lt;br /&gt;            except AttributeError:&lt;br /&gt;                name = model.split('.')[-1].lower()&lt;br /&gt;            self.manager_name = kwargs.pop('manager_name', 'for_%s' % name)&lt;br /&gt;            super(ForeignKey, self).__init__(*args, **kwargs)&lt;br /&gt;       &lt;br /&gt;        def contribute_to_class(self, cls, name):&lt;br /&gt;            super(ForeignKey, self).contribute_to_class(cls, name)&lt;br /&gt;           &lt;br /&gt;            class MyManager(Manager):&lt;br /&gt;                def __call__(self2, obj):&lt;br /&gt;                    return cls._default_manager.filter(**{self.name: obj})&lt;br /&gt;           &lt;br /&gt;            cls.add_to_class(self.manager_name, MyManager())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, a lot of the code is the same as before.  Most of the new code is in getting the mode's name, either through _meta, or through the last part of the string(i.e. User in "auth.User").  And now you will have a manager on your class, named either for_X where X is the name of the model the foreign key is to lowercase, or named whatever the kwarg manager_name is.&lt;br /&gt;&lt;br /&gt;So if your model has this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    teacher = LazyForeignKey(Teacher)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;You would be able to do:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    MyModel.for_teacher(Teacher.objects.get(id=3)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's all for today.  Since tonight is election night, tomorrow I'll probably post about my application election-sim, and about PyGTK and PyProcessing(aka multiprocessing).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-5484634966753561934?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/5484634966753561934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/more-laziness-with-foreign-keys.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5484634966753561934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/5484634966753561934'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/more-laziness-with-foreign-keys.html' title='More Laziness with Foreign Keys'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-7176062489626496619.post-7896070211999693688</id><published>2008-11-03T14:31:00.006-05:00</published><updated>2008-11-10T20:36:49.776-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='foreignkey'/><category scheme='http://www.blogger.com/atom/ns#' term='django'/><category scheme='http://www.blogger.com/atom/ns#' term='orm'/><category scheme='http://www.blogger.com/atom/ns#' term='models'/><title type='text'>Lazy User Foreign Keys(this is a double entendre)</title><content type='html'>A *very* common pattern in Django is for models to have a foreign key to django.contrib.auth.User for the owner(or submitter, or whatever other relation with User) and then to have views that filter this down to the related objects for a specific user(often the currently logged in user).  If we think ahead, we can make a manager with a method to filter down to a specific user.  But since we are really lazy we are going to make a field that automatically generates the foreign key to User, and gives us a manager, automatically, to filter for a specific User, and we can reuse this for all types of models.&lt;br /&gt;&lt;br /&gt;So what does the code look like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    from django.db.models import ForeignKey, Manager&lt;br /&gt;&lt;br /&gt;    from django.contrib.auth.models import User&lt;br /&gt;&lt;br /&gt;    class LazyUserForeignKey(ForeignKey):&lt;br /&gt;        def __init__(self, **kwargs):&lt;br /&gt;            kwargs['to'] = User&lt;br /&gt;            self.manager_name = kwargs.pop('manager_name', 'for_user')&lt;br /&gt;            super(ForeignKey, self).__init__(**kwargs)&lt;br /&gt;       &lt;br /&gt;        def contribute_to_class(self, cls, name):&lt;br /&gt;            super(ForeignKey, self).contribute_to_class(cls, name)&lt;br /&gt;           &lt;br /&gt;            class MyManager(Manager):&lt;br /&gt;                def __call__(self2, user):&lt;br /&gt;                    return cls._default_manager.filter(**{self.name: user})&lt;br /&gt;           &lt;br /&gt;            cls.add_to_class(self.manager_name, MyManager())&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So now, what does this do?&lt;br /&gt;&lt;br /&gt;We are subclassing ForeignKey.  In __init__ we make sure to is set to User and we also set self.manager_name equal to either the manager_name kwarg, if provided or 'for_user'.  contribute_to_class get called by the ModelMetaclass to add each item to the Model itself.  So here we call the parent method, to get the ForeignKey itself set on the model, and then we create a new subclass of Manager.  And we define an __call__ method on it, this lets us call an instance as if it were a function.  And we make __call__ return the QuerySet that would be returned by filtering the default manager for the class where the user field is equal to the given user.  And then we add it to the class with the name provided earlier.&lt;br /&gt;&lt;br /&gt;And that's all.  Now we can do things like:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;    MyModel.for_user(request.user)&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Next post we'll probably look at making this more generic.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/7176062489626496619-7896070211999693688?l=lazypython.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://lazypython.blogspot.com/feeds/7896070211999693688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://lazypython.blogspot.com/2008/11/lazy-user-foreign-keysthis-is-double.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7896070211999693688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/7176062489626496619/posts/default/7896070211999693688'/><link rel='alternate' type='text/html' href='http://lazypython.blogspot.com/2008/11/lazy-user-foreign-keysthis-is-double.html' title='Lazy User Foreign Keys(this is a double entendre)'/><author><name>Alex</name><uri>http://www.blogger.com/profile/14054821112394577330</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry></feed>
