Python Tales and Plone Stories

4teamwork

Setuptools Console_scripts Entrypoints and Buildout

If your Plone deployment story looks anything like ours, you probably have several Python packages that get pulled together by a project-specific buildout that lives in its own git repository.

While we try to keep those buildouts clean and pretty minimal, sometimes there’s need for little helper scripts that you want to execute from that buildout, but still should live in an appropriate package and therefore another repository.

Also, bin/ will most likely be in your .gitignore, so you can’t check in any scripts in that directory anyway.

Generating scripts in bin/ using collective.recipe.template would be an option, but this approach will either clutter your buildout .cfgs with inline scripts or the buildout repository with .py.in templates.

So here’s the way to go:

Using the setuptools console_scripts entry point, you can wire up any function (or other callable) with a console script that will be generated for you upon package installation:

1
2
3
4
5
entry_points="""
# ...
[console_scripts]
myscript = myproject.maintenance.cli:myfunc
""",

When a package with such an entry point is installed system-wide (for example using easy_install or python setup.py install) a console script in e.g. /usr/local/bin/myscript will be generated that calls myfunc() in the cli.py module of the myproject.maintenance package. myscript will therefore be in your $PATH, and can conveniently be used to expose functionality as a console command.

Similarly, if you’re installing such a package using virtualenv, a bin/myscript executable will be created inside the virtualenv.

Now, if you’re using buildout, zc.recipe.egg will allow you to make use of those entry points. You do that by specifiying the zc.recipe.egg:scripts recipe instead of just zc.recipe.egg:

1
2
3
4
5
6
7
[buildout]
parts = myproject

[myproject]
recipe = zc.recipe.egg:scripts
eggs = myproject
# ...

This will generate scripts for all the console_scripts entry points in that package, so for our example a bin/myscript will be created.

That will create all the scripts for entry points defined in the egg(s) listed explicitely in that zc.recipe.egg part. If you enable the dependent-scripts option however, all scripts for all dependent packages will be generated – nice!

1
2
3
4
5
6
7
[buildout]
parts = myproject

[myproject]
recipe = zc.recipe.egg
eggs = ...
dependent-scripts = true

So in the context of a Plone buildout, that could include bin/develop for mr.developer, bin/diazocompiler from diazo, bin/zptlint from zptlint – you get the idea.

Comments