I have a new toy. In Chicago.

It's my brand-new Virtual Private Server from ServerAxis. As someone else noted, ServerAxis may be the best-kept secret in VPS hosting. Despite their somewhat spartan website, they have good rates (I got the $45 a month one - you can't get a VPS for shared host prices! But it's worth it), Xen virtualization, and also offer dedicated hosting, in the event that your site becomes very popular.

I've heard that their support is excellent, but I've had no reason to contact them - everything has worked perfectly. My server hasn't had any downtime since I got it a couple weeks ago, and since it's Xen virtualization, I'm not going to have a leaky abstraction - I can't tell in the slightest1 what the other people on my server are doing.

If there are any other fellow Dreamhosters looking for a VPS host worthy of supplementing an overworked shared server - this is it. This blog, incidentally, will remain on DreamHost, because I don't really care about latency here, but for web applications, latency is a Bad Thing, so I'll be using the VPS there.

I do have a domain set up for it, but as of yet I'm still working on some webapps I will deploy there. I'm using Django, although in a form probably unrecognizable to most Django users - I'm not using Django models, nor their templating, but instead am using SQLAlchemy and Mako.

There are a few reasons I'm going with a stripped-down Django rather than Pylons, which might seem like the natural choice for something with so many swapped out components.

The most important is Django's request handling is much more intuitive than Pylons. A request is a parameter passed to a function, and the response is any file-like object; this makes writing non-template data to the request very Pythonic. Pylons has been making some headway in this; for example, in Pylons 0.9.5, writing a PDF to a request involved something like this (this is not tested code):

  1. class PDFMakerController(BaseController):
  2. def profile_pdf(self, id):
  3. # Get user object
  4. user = User.get(int(id))
  5. # Render the PDF
  6. pdf_data = self._render_profile_to_pdf(user)
  7. # Initialize the response
  8. resp = Response()
  9. # Set the header
  10. resp.headers['Content-Type'] = "application/pdf"
  11. resp.write(pdf_data)
  12. # flush/close the request, and return it
  13. resp.flush()
  14. resp.close()
  15. return resp

It improved significantly in 0.9.6:

  1. class PDFMakerController(BaseController):
  2. def profile_pdf(self, id):
  3. # Get user object
  4. user = User.get(int(id))
  5. # Render the PDF
  6. pdf_data = self._render_profile_to_pdf(user)
  7. # Set the header
  8. response.headers['Content-Type'] = "application/pdf"
  9. return pdf_data

It still relies on the request global variable. Global variables are OK in moderation (sorry CS teachers! It's much like the much-maligned goto), but in this case it's still awkward. Especially so since request isn't even imported by name (but rather by *), making searching the source file for the import statement impossible. Luckily, in most cases there will only be one * import in Pylons controllers, so you can backtrack through the files to find the origin of the variable, but that won't always be the case.

Unfortunately, it's not as good, in terms of clarity and simplicity (of implementation, of course) from the equivalent Django code:

  1. def profile_pdf(request, id):
  2. # Get the user object
  3. user = User.get(int(id))
  4. # Render the PDF
  5. pdf_data = _render_profile_to_pdf(user)
  6. response = HttpResponse(mimetype = "application/pdf")
  7. response.write(pdf_data)
  8. # Return the PDF
  9. return response

The unneeded class definition is removed; ditto for the global variable.

It may seem that something like the level of indentation is trivial in the grand scheme of programming. It is not. In fact, such "cosmetic" things are the most important difference between software nowadays. The importance of this to me will probably get clearer with the next reason I'm not using Pylons for this project:

Pylons requires too many directories. Django's directory structure is like so:


And that's it. All I need, and nothing more. Django uses a models.py file by default, but I prefer making it a folder instead, due to the way I use SQLAlchemy.

In contrast, Pylons' directory structure has no project name, while the appname is repeated twice; there is all kinds of cruft in both the upper and the lower appname/ directories. This is again somewhere that Pylons is improving; 3 rarely-used folders were removed in Pylons 0.9.6.

Again, this may seem like a shallow reason, but I'd argue the exact opposite; when attempting an ambitious project (which is what I'm doing) it's very easy to become overwhelmed with architectural decisions early on if you don't know what you're doing. And I don't know what I'm doing - (almost) no one does. So I'm trying to minimize the cost of early mistakes.

As I've been reading ANSI Common Lisp, I might as well point out that this is one of the first things that Graham points out in the book. Lisp makes messing up trivial, and it is that throw-away attitude that gives it it's famed rapid prototyping. I'm aiming for the same attitude in Python: Plan to throw one away, because you will anyway, and then throw away the second and third, too.

  1. There was assorted grumbling on some forums that other virtualization software - such as Virtuozzo - can be somewhat deceiving. I'd assume that it 'lies' about how much free memory and CPU you have, on the assumption that the physical sever will rarely be at 100% usage, but leaking in that if multiple users max out their resources, it behaves more like a shared server, and service slows down for everyone. This doesn't happen for Xen; I have one gig of RAM in my VM, and no other VMs can take it away.