Mutlipart file upload in Turbogears 1.0.x

March 16th, 2009

When uploading files to Beep My Stuff using SWFUpload I was getting this error:

data = self._sock.recv(self._rbufsize)
timeout: timed out

Uploading files to a TG1.0.x app is easy unless you want to do something nice like use SWFUpload to allow multiple files to be selected for upload.

The reason for this is that Flash has interpreted RFC 2046 one way and they python libraries/CherryPy interpret it another. Fortunately this is fixed in Turbogears 1.1+ but I am using 1.08 at the moment for Beep My Stuff so I needed a fix.

There is a patch in CherryPy that can be used to fix the problem and Chris Arndt has created a patch for TG1.1. You can easily apply the same code to your TG1.0x app to fix this problem as well.

Visit here and copy the contents of safemutlipart.py into a file in your project. Then you need to install the filter when Turbogears is starting up. Something like this will do the trick:

#force the multipart filter to be installed.
def install_safemutlipartfilter():
    from cherrypy import root
    root._cp_filters.extend([SafeMultipartFilter()])
 
def install_multipart_filter():
    from turbogears.startup import call_on_startup
    log.info("Installing multipart filter")
    call_on_startup.append(install_safemutlipartfilter)

Note that the code in SafeMultiPartFilter needs a config change to turn it on so add

safempfilter.on = True

to your app.cfg

hope that helps :)

Replaying apache log files with python and twill

October 2nd, 2008

In order to test for a memory leak in New Metal Army I needed to replay my apache log files against my test server. Using Twill this was a doddle.

The only slightly icky thing about the script is the regular expression to parse a line from the apache log file (in Combined Log Format). I got this from RegExp Buddy (pretty much the only reason I run Windows nowadays) but I am sure you can get similar expressions from other regular expression libraries.

Anyway, I'm just chucking this out there incase someone else finds it useful.

import re
import twill
 
test_server="my.test.server.com"
 
reobj = re.compile(r'^(?P<client>\S+)\s+(?P<auth>\S+\s+\S+)\s+\[(?P<datetime>[^]]+)\]\s+"(?:GET|POST|HEAD) (?P<file>[^ ?"]+)\??(?P
<parameters>[^ ?"]+)? HTTP/[0-9.]+"\s+(?P<status>[0-9]+)\s+(?P<size>[-0-9]+)\s+"(?P<referrer>[^"]*)"\s+"(?P<useragent>[^"]*)"$', re.MULTILINE)
browser = twill.get_browser()
 
def filter_url(url):
    return False
 
for line in open("apache.log"):
    match = reobj.search(line)
    if match is None:
        continue
 
    f = match.group("file")
    p = match.group("parameters")
    d = match.group("datetime")
    path = "?".join([f, p]) if p else f
 
    url = test_server+path
 
    if(filter_url(url)):
        continue
 
    try:
        print d, url
        browser.go(url)
    except ValueError:
        #this comes from twill parsing the HTML and it being malformed.
        #I don't really care about that, as long as I get the page.
        pass
 

SQLAlchemy-Migrate upgrade scripts in a transaction

July 27th, 2008

SQLAlchemy Migrate is a really good tool for managing database upgrades for SQLAlchemy centric projects. I've been using it for 6 months on New Metal Army and I haven't had a screwed website upgrade yet!

For those that don't know SQLAlchemy-migrate allows you to version control database changes and easily upgrade and downgrade a database. Basically you write a python script with two functions: upgrade and downgrade. You test the script against the database, commit it to the SQLAlchemy-migrate version repository (not to be confused with your source control mechanism). Finally you upgrade your development database.

When it the time comes to deploy the new application you simply ask sqlalchemy-migrate to upgrade your database to the current version. sqlalchemy-migrate reads the current version of your schema from the database (via a custom table it inserts) and proceeds to upgrade your schema by running each upgrade script in turn.

Often you want your upgrades and downgrades to run within a transaction. Not because you expect them to fail but because while writing them you don't wont to leave the database partially upgraded or downgraded if your script fails. To do this I wrote a transaction decorator. Here is a template for an upgrade script:

  1. #!/usr/bin/env python
  2. # encoding: utf-8
  3.  
  4. from datetime import datetime
  5. from sqlalchemy import *
  6. from migrate import *
  7. from migrate.changeset import *
  8.  
  9. metadata = MetaData(migrate_engine)
  10.  
  11. def transaction(f, *args, **kwargs):
  12. def wrapper(*args, **kwargs):
  13. connection = migrate_engine.connect()
  14. transaction = connection.begin()
  15. try:
  16. result = f(*args, **kwargs)
  17. transaction.commit()
  18. return result
  19. except:
  20. transaction.rollback()
  21. raise
  22. finally:
  23. connection.close()
  24.  
  25. wrapper.__name__ = f.__name__
  26. wrapper.__dict__ = f.__dict__
  27. wrapper.__doc__ = f.__doc__
  28. return wrapper
  29.  
  30. @transaction
  31. def upgrade():
  32. pass
  33.  
  34. @transaction
  35. def downgrade():
  36. pass
  37.  

I fill in the upgrade and downgrade functions and I'm done :)

I include the decorator in every script as it's good practice to make your scripts as independent as possible. If you imported it from a module you may improve it in the future and inadvertently break your old downgrade scripts.

I hope this helps you version control your database schema and data.