Debian Packaging Python Scripts by Example
Here we build off the Debian Packaging by Example article and create packages that deliver Python scripts, i.e., command line interface (CLI) applications written in Python.
Requirements
To follow along, you will need a machine (lets call this our build machine) running a Debian-based operating system, e.g., the popular Ubuntu. This tutorial was written using a Google Cloud GCE instance based on the Canonical, Ubuntu, 24.04 LTS Minimal, amd64 noble minimal image built on 2024–09–26 image.
The machine needs a number of Debian packages and can be installed using:
sudo apt update
sudo apt install build-essential -y
sudo apt install devscripts -y
sudo apt install debhelper -y
sudo apt install python3.12-venv -y
The first three packages are for building Debian packages. Assuming your machine, as did mine, has Python version 3.12 pre-installed, the last package enables the management of Python virtual environments.
We will assume the machine (lets call this our target machine) where we want to install the Debian package on already has Python 3.12 (or greater).
The Python Script
We avoid the complexity of creating our own Python package by using an the cowsay Python package; essentially provides the cowsay Python script.
We first need to understand what Python virtual environments are and what problem they solve; from the topic in the Python tutorial:
Python applications will often use packages and modules that don’t come as part of the standard library. Applications will sometimes need a specific version of a library, because the application may require that a particular bug has been fixed or the application may be written using an obsolete version of the library’s interface.
This means it may not be possible for one Python installation to meet the requirements of every application. If application A needs version 1.0 of a particular module but application B needs version 2.0, then the requirements are in conflict and installing either version 1.0 or 2.0 will leave one application unable to run.
The solution for this problem is to create a virtual environment, a self-contained directory tree that contains a Python installation for a particular version of Python, plus a number of additional packages.Creating a Python Package and Debian Packaging It
With virtual environments in mind, we first explore installing the cowsay Python script manually.
sudo mkdir /opt/venv
sudo python3 -m venv /opt/venv/my-cowsay
sudo /opt/venv/my-cowsay/bin/python3 -m pip install cowsay
sudo ln -s /opt/venv/my-cowsay/bin/cowsay /usr/bin/cowsay
and indeed we get the following:
$ cowsay -t 'Hello World!'
____________
| Hello World! |
============
\
\
^__^
(oo)\_______
(__)\ )\/\
||----w |
|| ||
This solution takes advantage of the virtual environments feature whereby we can both install Python packages and execute scripts in virtual environments without first activating the virtual environment. One important detail is the shebang of the cowsay script points to the Python interpreter in the virtual environment.
#!/opt/venv/my-cowsay/bin/python3
Before continuing, we want to clean up the manual install.
sudo rm /usr/bin/cowsay
sudo rm -f -r /opt/venv
Now to creating a Debian package that does this exact same thing. Here we closely follow the steps in the Debian Packaging by Example article.
Upstream Tarball
The key to this article is the three install lines in the single file, Makefile, in the upstream tarball.
VENV = my-cowsay
all:
echo 'no op build rule'
install:
python3 -m venv $(DESTDIR)/opt/venv/$(VENV)
$(DESTDIR)/opt/venv/$(VENV)/bin/python3 -m pip install cowsay
sed -i 's/$(shell echo $(DESTDIR) | sed 's/\//\\\//g')//g' $(DESTDIR)/opt/venv/$(VENV)/bin/cowsay
Some observations:
- The indents are a single tab; not spaces
- Because the Makefile is executed on the build machine using a staging folder, the shebang of the cowsay script initially points to the Python interpreter in the staging folder (incorrect for the target machine); the fix is a bit of shell magic to strip out the staging folder from the path in the shebang
At this point, the top level folder has the folder with its files we created and the tarball of it in it.
Add the Debian Packaging Files
We follow along creating the debian folder and all the required files with following additions:
The my-cowsay.dirs file ensures that the required folders exist.
opt/venv
usr/bin
The my-cowsay.links file ensures that the soft links exist.
/opt/venv/my-cowsay/bin/cowsay /usr/bin/cowsay
At this point we have the following folders and files:
If we got all this right, we can successfully run the command to build the Debian package.
debuild -us -uc
The Binary Package
The file, hello-world_1.0–1_amd64.deb, is the binary package (here for the amd64 architecture). We can install it onto our machine using:
sudo dpkg -i ./my-cowsay_1.0-1_amd64.deb
If we got things right, we can run the cowsay command (the earlier example) and get the image of the cow saying Hello World!.
Wrap Up
Wow; that was a lot to explain what essentially amounts to three lines in a makefile.