Running trac-admin from python

Recently, I’ve been trying to speed up my Subversion post-commit hooks.  I have several things which are run from the hook, and the number of separate commands leads to a bunch of fork() calls (or clone(), whatever).  Several of the scripts are already Python, so I figured I’d just write the hooks themselves in Python, making it so that the python interpreter would only need to start once and allowing the separate methods to be pre-compiled python.  This should decrease overall execution time, making the end-user experience slightly better overall by decreasing the time they have to wait on the server.  We’re talking about fractions of a second, but I have some operations which bulk-create directories in SVN or otherwise cause tens or hundreds of new revisions to be created at one time (which is necessary for the way some of my integration processes work), so it actually adds up.

This is also an excuse for me to learn Python, so bear with me if the code below is horrible.  Actually, don’t bear with me – leave a comment letting me know how it should have been done. :)

I’ll put the general code up here when it’s all done.  For now, though, one of the things I wanted to do was to run the trac-admin commands directly rather than having to make a system call to launch another python binary from within python.  I couldn’t find any documentation, but reading through the code, I ended up with two ways to do it.

One way is to use the console command tool, which is what runs when you type “trac-admin /path/to/trac command arg”:

import trac.admin.console
adm = trac.admin.console.TracAdmin('/srv/trac/instances/abc/')
adm.onecmd('permission list')

The other way is to skip over some of the line parsing and run what that eventually runs:

from trac.env import Environment
from trac.admin import AdminCommandManager
mgr = AdminCommandManager(
        Environment('/srv/trac/instances/abc/') )
mgr.execute_command('permission', 'list')

I’m not sure which is more portable to future versions, or if either one is.  But the AdminCommandManager method is ever so slightly faster, probably because there’s slightly less overhead.  I ended up putting that into a simple class like this:

from trac.env import Environment
from trac.admin import AdminCommandManager
class TracWrap:
    def __init__(self, env):
        self.mgr = AdminCommandManager(Environment(env))

    def trac_admin(self, *cmd):

And then in my post_commit hook script, I do this, which I think is more readable:

trac = TracWrap('/srv/trac/instances/abc/')
trac.trac_admin('changeset', 'added', repo, rev)

I couldn’t find anything when I searched Google for this, so maybe this will help someone else who’s using the same keywords I was using. :)