In the post Python Packaging: setuptools and eggs, I described how to use setuptools to create a distributable egg. Installing the egg would provide:
- a set of python packages and modules, usable as a library
- a set of “scripts”: small programs that live in the user’s bin directory, where ever that is; these, for the most part invoke functions within the packages
While setuptools can generate these scripts for us, sometimes that does not cut it. Maybe we really do want to create scripts ourselves and get them installed in the user’s `bin` directory.
Writing scripts yourself
In the last post we got setuptools to generate three scripts for us: rundog, rungendibal, runbjarne. Let us hand-craft a script that does the work of any of these, depending on a command line argument. Let’s call our script ‘runany’: it will take one argument—”dog”, “gendibal”, or “bjarne”—and run the appropriate script for the argument. Here is our script:
import sys, os
os.execlp("run" + which)
And we tell setuptools (actually, the underlying distutils) about it:
Now, we can build our egg. When the user installs the egg, he would have the following scripts available:
- rundog, rungendibal, runbjarne: these are auto-generated by setuptools
- runay: this is a wrapper script that setup’s Pythons path and calls our own script above.
The user can run our runany script like this:
$ runany dog
$ runany bjarne
Hello, C++ World!
Let’s see what setuptools did. First, it put a runany script in the user’s path. What’s inside?
$ cat `which runany`
# EASY-INSTALL-SCRIPT: 'Speaker==0.2dev','runany'
__requires__ = 'Speaker==0.2dev'
If you were expecting to see the code we wrote above and are surprised, join the club. This script uses the
pkg_resources.run_script to actually call our code. The rest is just ensuring that the proper version of the egg is being referenced.
Why does it do so? So that the user can have multiple versions of our package installed and be able to use them.
Where is our script anyway? It is here:
$ ls /lib/python2.5/site-packages/Speaker-0.2dev-py2.5-linux-x86_64.egg/EGG-INFO/scripts/
where install_dir refers to where ever the user wanted our egg to be installed.
In conclusion, we wrote some custom code, told setuptools where to find it (using `scripts=[...]` directive in setup.py), created an egg, installed it, and everything works.
You might have noticed above that the custom script we wrote was in Python. This is not an accident: the distutils/setuptools directive `scripts=…` that we used to get our stuff packaged and installed expects to be given python scripts. Actually, distutils can handle non-Python scripts: it will copy them verbatim without wrapping them or adjusting them in anyway. However, setuptools always wraps scripts in a way that non-Python scripts are not supported. This can be considered a bug in setuptools. So, when using setuptools, non-Python scripts are out. This is not so bad: why would you want to write a shell or some other script for doing something that can be done in Python?
Non-package Data Files
What if you wanted an init-script to go into `/etc/init.d/’, or some config file to go into ‘/etc/’? setuptools won’t do that for you either. Setuptools will keep your files bundled inside the egg. But you can provide the user with a script that will extract and copy the files to arbitrary locations, on their discretion. See http://peak.telecommunity.com/DevCenter/setuptools#non-package-data-files.