Python Packaging
Packaging a Python project for distrubtion is just not straight forward.
Some parameters:
- Distributing a library or an application
- Distributing the source or byte-code, or some sort of executable
- Including a Python run-time with the application, or is it up to the user to install Python and know how to run your application inside Python
- How to avoid all the pit-falls that come with library paths, external packages, dependencies, Python v2 vs v3 compatibility, and the target host/OS it will be run on.
A simple Google search reveals that I’m not alone , in , my , trials.
My real-world example is the application I wrote as a front-end to the dar
backup utility, darfortie
. It started out as a bash script, quickly outgrew that and became a Python script with a few supporting modules as well as external dependancies, not the least of which is the dar
application itself.
The PyPI distribution described below works as it should, but it took me all of the steps below to realize that my problem was that I was installing it using pip
as a normal user, instead of sudo pip...
and so the execuatable was not ending up in a directory in my system PATH
.
Originally I had published the project as source code and as two executable formats, as described in this ablepear post, which I summarize below.
Source format
The source format lives in a directory
/home/ken/darfortie/darfortie
and contains three files:
- darfortie_params.py
- darfortie_previous_file.py
- _main_.py
The _main_.py file imports the other two files and contains a main()
, along with the requisite
if __name__ == '__main__':
main()
So, if I’m in the directory /home/ken/darfortie
, I can run the app using
$ python darfortie
A shell script also works:
#!/bin/bash
cd /home/ken/darfortie
/usr/bin/python darfortie $@
Executable format
The first executable format is a zip file that contains the same three files, above:
- darfortie_params.py
- darfortie_previous_file.py
- _main_.py This app runs in a similar way, I can run the app using:
$ python /home/ken/darfortie/darfortie.zip
The second executable format is zip file renamed as darfortie
, has its attributes set to executable and with the line #!/usr/bin/env python
prepended to the file. This runs as:
$ python /home/ken/darfortie/bin/darfortie
All of this is fine and works as expected. Enter PyPI…
PyPI
PyPI is the defacto repository for Python software, and so to make my project available this way, I made some changes to the project.
- PyPI only supports ReStructured text (.rst) for the readme. Github supports both .rst and Markdown (.md). So I converted my readme file to .rst and updated the contents, removing the information about running darfortie from source, or zip, as I described above, and changed it to instruct how to install the project using
pip
which is the application that interfaces with the PyPI repository and installs applications on a client machine. - Renamed the
__main__.py
module todarfortie_main.py
. - Created a new module
__init__.py
which simply imports the three modules of the project:
darfortie_main
darfortie_params
darfortie_previous_file
- Created a setup.py file which PyPI uses to register the application.
This was uploaded and as a result you can run this to install darfortie on a machine:
pip install -i https://pypi.python.org/pypi darfortie
If this is done as a normal user, then the install puts the package in
/home/ken/.local/lib/python2.7/site-packages/darfortie
and creates an execuatable such that the application can be run by:
$ ./local/bin/darfortie
If installed as root
, then the install is done into
/usr/local/lib/python2.7/dist-packages/darfortie
and the executable is created as:
/usr/local/bin/darfortie
which is normally on the system PATH.