Random notes from mg

a blog by Marius Gedminas

Marius is a Python hacker. He works for Programmers of Vilnius, a small Python/Zope 3 startup. He has a personal home page at http://gedmin.as. His email is marius@gedmin.as. He does not like spam, but is not afraid of it.

Tue, 15 Sep 2009

Pylons with zc.buildout, continued

Last time I mentioned that running bin/buildout with the -N flag makes it run faster (since it skips looking for newer versions to upgrade). You can tell buildout to do this by default by putting 'newest = false' into the [buildout] section of buildout.cfg. We'll be running bin/buildout a lot now, since we'll be making changes to the project environment, so this will save wear and tear on the '-', 'N' and Shift keys. (And, by the way, I'm not trying to soak up Google juice by repeating the word 'buildout' a lot, honest!)

I will omit bzr commits from this narrative as it's getting long; you can assume that every self-contained change was committed separately.

tests

First, I want a bin/test script to run the test suite. Pylons uses nose, so we need to tell buildout to install the nosetests script (under a different name, since I'm used to typing bin/test no matter what test runner a project happens to use):

$ bzr diff
=== modified file 'buildout.cfg'
--- buildout.cfg	2009-09-15 19:49:11 +0000
+++ buildout.cfg	2009-09-15 19:49:18 +0000
@@ -8,5 +8,8 @@
 recipe = zc.recipe.egg
 eggs = Pylons
        PasteScript
+       nose
        asharing
 interpreter = python
+scripts = paster
+          nosetests=test

$ bin/buildout
...
Generated script '/tmp/AlliterationSharing/bin/paster'.
Generated script '/tmp/AlliterationSharing/bin/test'.
...
$ bin/test

----------------------------------------------------------------------
Ran 0 tests in 0.276s

OK

ctags

Documentation is good, but sometimes you want to look at the source code of the framework. There's a tool called ctags that builds a database of identifiers. The popular text editors Vim and Emacs can then use the tags database to jump to a definition of any name with a single keystroke (Ctrl-] in vim, M-. in emacs).

Building the tags database is complicated by each Python package being installed into a separate directory. There's a buildout recipe called z3c.recipe.tag that finds those directories and lets you build a unified tags file. We'll also ask buildout to make sure it unzips any packages distributed as .egg files, since ctags doesn't process those:

$ bzr diff
@@ -1,8 +1,9 @@
 [buildout]
 develop = .
-parts = pylons
+parts = pylons ctags
 
 newest = false
+unzip = true
 
 [pylons]
 recipe = zc.recipe.egg
@@ -13,3 +14,7 @@
 interpreter = python
 scripts = paster
           nosetests=test
+
+[ctags]
+recipe = z3c.recipe.tag:tags
+eggs = ${pylons:eggs}

$ bin/buildout
...
Generated script '/tmp/AlliterationSharing/bin/ctags'.
...
$ bin/ctags

omelette

ctags lets you find classes and functions by name; it doesn't let you find packages or modules. There's another recipe, collective.recipe.omelette that creates a tree of symlinks mirroring the Python package structure (here 'unzip = true' also comes in handy):

$ bzr diff
=== modified file 'buildout.cfg'
--- buildout.cfg	2009-09-15 20:04:42 +0000
+++ buildout.cfg	2009-09-15 20:05:30 +0000
@@ -1,6 +1,6 @@
 [buildout]
 develop = .
-parts = pylons ctags
+parts = pylons ctags omelette
 
 newest = false
 unzip = true
@@ -18,3 +18,7 @@
 [ctags]
 recipe = z3c.recipe.tag:tags
 eggs = ${pylons:eggs}
+
+[omelette]
+recipe = collective.recipe.omelette
+eggs = ${pylons:eggs}

$ bin/buildout 
...
$ ls -l parts/omelette
...

The symlink tree is created under parts/omelette/. For example, if you want to see what webhelper tags were available, you can open parts/omelette/webhelper/html/builder.py in your editor and see.

Makefile

This is getting long (and not everyone may be interested1), but one long post is easier to skip than five medium ones in a row, so I'll continue.

1 Sorry, Planet Maemo! There's an RSS feed of posts tagged 'maemo', if you can figure out the URL, which is very well hidden by PyBlosxom, *sigh*.

Wouldn't it be nice if new developers could check out your project and start it up with just a couple of commands? Make is a time-tested tool that works well for this:

$ cat Makefile
# Just remember that you need to use real tabs, not spaces, in a Makefile

PYTHON = python

.PHONY: all
all: bin/paster

.PHONY: run
run: bin/paster
        bin/paster serve development.ini --reload

.PHONY: test check
test check: bin/test
        bin/test

.PHONY: tags
tags: bin/ctags
        bin/ctags

bin/paster bin/test bin/python bin/ctags: bin/buildout
        bin/buildout

bin/buildout: bootstrap.py
        $(PYTHON) bootstrap.py

Now all you need to do after checking out is run 'make' to set up a working development environment. 'make run' or 'make test' will also do that, if necessary, so this one-liner is sufficient to get a working Hello World application on port 5000:

$ bzr branch lp:~mgedmin/+junk/AlliterationSharing && cd AlliterationSharing && make run

Try it! You'll get a Bazaar branch with all the history of this little blog project.

posted at 23:31 | tags: , , | permanent link to this entry | 7 comments
What? A makefile to run buildout? That really surprised me. Buildout itself is basically a make-replacement, so writing a makefile to run "python bootstrap.py;bin/buildout" looks strange to me.

That would be the equivalent of writing a makefile to run "./configure;make;make install".


I must say that the paster line is somewhat elaborate, so a wrapper around that is handy.  Was that the main reason for the makefile?
posted by Reinout van Rees at Wed Sep 16 12:14:02 2009
Reinout: they're both build tools, but the things you can do easily with buildout and the things you can do easily with a Makefile have little overlap.

The Makefile is not required, but I think it may be helpful.  Make is a widely-known tool; buildout isn't yet (and may never be, being confined to a smaller niche).  People see a Makefile, they know they can run 'make'.  python bootstrap.py && bin/buildout is not that obvious.

I admit that the paster line was the final straw for me personally.  I wanted to have buildout create a bin/run as a simple shell script (or a Python equivalent) running that paster line.  I balked at trying to figure out how to do that with buildout (uh, I need a recipe ... how do I find it?  Oh dear.  Topic for a new lazyweb post, I suppose).
posted by Marius Gedminas at Wed Sep 16 13:50:57 2009
I use:

bin/paster serve development.ini --reload --monitor-restart

so that the server would not stop after the first syntax error you save to disk.

Also - scripts depend on buildout.cfg and setup.py, because you want to rerun buildout every time any of those change.
posted by Ignas Mikalajūnas at Wed Sep 16 14:57:15 2009
Marius-
Having started messing with tags I'm curious.  Do you only use it for Python in your Pylons project?  Do you have it working with html/css/js?

cheers
posted by matt harrison at Wed Sep 16 16:47:58 2009
@Matt: exuberant-ctags supports 41 languages; JavaScript and HTML are in that list (I had no idea!), CSS isn't.

For some reason the tags db built by z3c.recipe.tag doesn't contain anything about .js/.html files in my project's public/ subdir, I don't know why.  I see a few tags about Weave's HTML documentation; it appears that ctags indexes <a name="identifier">.

Previously I've found --extra=+q (create tags for each source file) useful (you can do :tag yourcontrollername.py), but this feature seems to be broken in ctags 5.7.
posted by Marius Gedminas at Wed Sep 16 17:20:43 2009
I think when I created the tags recipe, there were problems generating tags for javascript used in Zope3, I think exuberant-ctags were crashing so I disabled javascript tags.

Don't know about html.

Also - I keep meaning, but fail to find the time to update idutils file to include mako templates when generating the index.
posted by Ignas Mikalajūnas at Sun Sep 20 17:39:02 2009
Here's another way to create a `paster serve` run command:


[paster-serve]
recipe = zc.recipe.egg
eggs = ${pylons:eggs}
arguments = args=['serve', '--reload', 'ini/development.ini']
scripts = paster=serve


The value of `arguments` is Python source that's inserted into the bin/serve script file as the arguments to the entry point function.

Found here: http://pypi.python.org/pypi/zc.recipe.egg#script-generation
posted by Wyatt Baldwin at Sat Sep 26 01:41:27 2009

Name (required)


E-mail (will not be shown)


URL


Comment (some HTML allowed)