Once you've collected all the bits and bobs for your application, you start using distutils by writing a special Python script, customarily named setup.py. You can make your setup.py script as complex as you want, but typically it is quite simple.
The setup.py script will then call setup from distutils.core with lots of arguments, including meta-data about the application or module to install, and a list of stuff to install.
The developer uses the setup.py script to create the package, and the user uses it to install the package.
Example 26-2. setup.py - a sample setup script
#!/usr/bin/env python from distutils.core import setup setup(name = "kalam", version = "1.0", description = "Kalam - the extensible Python editor", author = "Boudewijn Rempt", author_email = "boud@rempt.xs4all.nl", url = "http://www.valdyas.org", packages = ["charmap", "kalamlib", "typometer", "workspace", ""], data_files = [("kalam/data", ["data/Blocks.txt"]), ("kalam/pixmaps", ["pixmaps/listspace.png", "pixmaps/splitspace.png", "pixmaps/stackspace.png", "pixmaps/tabmanager.png", "pixmaps/workspace.png"])], scripts = ["kalam","kalam.bat"], long_description = """ Kalam is a plain-text editor. It is written in Python using the PyQt GUI toolkit as an example and tutorial for the book GUI programming with Python and Qt, published by Opendocs. """ )
The setup.py is the place to specify all executable parts of your application, and some metadata. Let's examine all parts:
name: the name of the application
version: the version number (major, minor, wee number)
description: a short description of the application
author: the person responsible for the application
author_email: his email address
url: website where the application is hosted
packages: a list of Python modules (directories that contain a set of Python files listed in a __init__.py file). In the case of Kalam these are the modules for the character map, the type-o-meter and the set of workspace options. The additional module, kalamlib, contains all the real Kalam code.
py_modules: a list of Python files. Note that if you include both the packages and thepy_modules keyword only the latter will be used.
data_files: this is a list of files that are not executable code. These files will be installed in a default place, like /usr/share on Linux. You must also include all these files in MANIFEST.in, otherwise they won't be packaged.
scripts: this is a list of python script files. If you use #!/usr/bin/python as the first line of a script to make it executable on Unix, Distutils will change that to the location of Python on the user's machine.
long_description: a longer description of the application. This is used when you create an rpm package.
There are other options more concerned with distributing C or C++ extension modules you have created. I don't cover them here.
Finally, a word of warning: if you are experimenting with setup.py, you will notice that a file called MANIFEST has been created. Always remove this file after creating a distribution. It is a kind of cache that lists the set of files that should be included; if you change this set, distutils will still read MANIFEST instead of your changes in setup.py.
Despite the data_files option to setup(), it is still necessary to provide a second file that contains a list of extra, non-Python files that need to be distributed. This file is called MANIFEST.in (mind the capitalization), and employs its own set of keywords to specify files to include or exclude.
Example 26-3. MANIFEST.in
include kalam include kalam.bat recursive-include data * recursive-include pixmaps * recursive-include dialogs *
Here, we include the kalam starter script and the kalam.bat and batch file. Then we recursively include everything in the directories data, pixmaps and dialogs. (The latter is not absolutely necessary for running the application, but it can't hurt to give people access to our dialog designs.)
The options available for MANIFEST.in are:
include pat1 pat2 ...
exclude pat1 pat2 ...
recursive-include dir pat1 pat2 ...
recursive-exclude dir pat1 pat2 ...
global-include pat1 pat2 ...
global-exclude pat1 pat2 ...
prune dir
graft dir
The setup.py script takes myriad command-line options. You can also create a setup.cfg file that contains the most important options. Amongst those options are a number that tell the installer to install the application in a specific place. The user might need to edit these to reflect his preferences. For Unix, a good default is:
[install] install_lib=/usr/local/share/kalam install_data=/usr/local/share/kalam install_scripts=/usr/local/bin
All Python files (everything that is mentioned in the py_modules or packages argument in setup.py) will be installed in the install_lib directory. Everything that is mentioned in the data_files argument will be installed in the install_data directory. Likewise, everything that is included in the scripts argument will be installed in install_scripts.
We are now ready to create the source distribution. This is a simple, one-line command:
boud@calcifer:~/src/kalam > python setup.py sdist
The distutils will then spew a lot of text on the screen, and deliver your package in a subdirectory named dist:
boudewijn@maldar:~/doc/pyqt/ch18/kalam > python setup.py sdist running sdist reading manifest template 'MANIFEST.in' writing manifest file 'MANIFEST' creating kalam-1.0 creating kalam-1.0/charmap creating kalam-1.0/data creating kalam-1.0/dialogs creating kalam-1.0/kalamlib creating kalam-1.0/pixmaps creating kalam-1.0/typometer creating kalam-1.0/workspace making hard links in kalam-1.0... hard linking README -> kalam-1.0 hard linking edmund.py -> kalam-1.0 hard linking kalam -> kalam-1.0 hard linking kalam.bat -> kalam-1.0 hard linking setup.cfg -> kalam-1.0 hard linking setup.py -> kalam-1.0 hard linking sitecustomize.py -> kalam-1.0 hard linking startup.py -> kalam-1.0 hard linking charmap/__init__.py -> kalam-1.0/charmap hard linking charmap/charmap.py -> kalam-1.0/charmap hard linking data/Blocks.txt -> kalam-1.0/data hard linking dialogs/frmfindreplace.ui -> kalam-1.0/dialogs hard linking dialogs/frmsettings.ui -> kalam-1.0/dialogs hard linking kalamlib/__init__.py -> kalam-1.0/kalamlib hard linking kalamlib/configtest.py -> kalam-1.0/kalamlib hard linking kalamlib/dlgfindreplace.py -> kalam-1.0/kalamlib hard linking kalamlib/dlgsettings.py -> kalam-1.0/kalamlib hard linking kalamlib/docmanager.py -> kalam-1.0/kalamlib hard linking kalamlib/docmanagertest.py -> kalam-1.0/kalamlib hard linking kalamlib/frmfindreplace.py -> kalam-1.0/kalamlib hard linking kalamlib/frmsettings.py -> kalam-1.0/kalamlib hard linking kalamlib/kalamapp.py -> kalam-1.0/kalamlib hard linking kalamlib/kalamconfig.py -> kalam-1.0/kalamlib hard linking kalamlib/kalamdoc.py -> kalam-1.0/kalamlib hard linking kalamlib/kalamview.py -> kalam-1.0/kalamlib hard linking kalamlib/macromanager.py -> kalam-1.0/kalamlib hard linking kalamlib/macromanagertest.py -> kalam-1.0/kalamlib hard linking kalamlib/main.py -> kalam-1.0/kalamlib hard linking kalamlib/resources.py -> kalam-1.0/kalamlib hard linking pixmaps/fileprint.xpm -> kalam-1.0/pixmaps hard linking pixmaps/find.png -> kalam-1.0/pixmaps hard linking pixmaps/listspace.png -> kalam-1.0/pixmaps hard linking pixmaps/listspace.xpm -> kalam-1.0/pixmaps hard linking pixmaps/splitspace.png -> kalam-1.0/pixmaps hard linking pixmaps/splitspace.xpm -> kalam-1.0/pixmaps hard linking pixmaps/stackspace.png -> kalam-1.0/pixmaps hard linking pixmaps/stackspace.xpm -> kalam-1.0/pixmaps hard linking pixmaps/tabmanager.png -> kalam-1.0/pixmaps hard linking pixmaps/tabmanager.xpm -> kalam-1.0/pixmaps hard linking pixmaps/workspace.png -> kalam-1.0/pixmaps hard linking pixmaps/workspace.xpm -> kalam-1.0/pixmaps hard linking typometer/__init__.py -> kalam-1.0/typometer hard linking typometer/typometer.py -> kalam-1.0/typometer hard linking workspace/__init__.py -> kalam-1.0/workspace hard linking workspace/listspace.py -> kalam-1.0/workspace hard linking workspace/splitspace.py -> kalam-1.0/workspace hard linking workspace/stackspace.py -> kalam-1.0/workspace hard linking workspace/tabmanager.py -> kalam-1.0/workspace hard linking workspace/workspace.py -> kalam-1.0/workspace creating dist tar -cf dist/kalam-1.0.tar kalam-1.0 gzip -f9 dist/kalam-1.0.tar removing 'kalam-1.0' (and everything under it) boudewijn@maldar:~/doc/pyqt/ch18/kalam >
That's it—a nice, clean and complete source distribution of Kalam. You can generate both zip archives and gzipped tarballs by providing options on the command line:
boudewijn@maldar:~/doc/pyqt/ch18/kalam > python setup.py sdist --formats=gztar,zip
The options are zip, gztar, bztar, ztar and tar, for zipfiles, gzipped tarfiles, bzipped tarfiles, compressed tarfiles and plain tar files.
Installing a source archive is a simple matter of unpacking the archive and executing the following command:
boudewijn@maldar:~/doc/pyqt/ch18/kalam/dist/kalam-1.0 > python setup.py install
Distutils will copy everything to the location designated in setup.cfg, and kalam will be ready to run!.