Starting with distutils.

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.

setup.py

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:

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.

MANIFEST.in

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:

setup.cfg

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.

Creating the source distribution

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

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!.