Python Packaging with Entry Points and pybuild

I needed to package an application consisting of a Python package and a corresponding commandline script. Starting from scratch I was able to use some modern techniques for this: Entry Points and pybuild.

Entry Points

Modern versions of setuptools can create a commandline script for you with very little effort. You still need to handle any commandline argument parsing and such but the script will be created and dropped in the appropriate place for you.

The setup.py features the usual setup function but we then need to pass entry_points to it. I think an example is the easiest way to show how it works.

from setuptools import setup, find_packages

setup(
    # ...
    packages=find_packages(),
    entry_points={
        'console_scripts': [
            'nameofthescript = mylib.somemodule:run_from_cli',
        ]
    },
    # ...
)

Using pip to install this as a test created a new script nameofthescript and upon calling it did run the function run_from_cli from the module mylib.somemodule.

pybuild

With the setup-file in place we can start to create our Debian package.

We need a new version of debhelper for this to work. With my primary targets are Ubuntu 16.04 and Debian 8 machines I decided to require at least version 9 as build requirement in debian/control. And as we want to package a Python application we also need to depend on dh_python for the build.

In the debian/rules the call to dh has to be extended with --with python3 --buildsystem=pybuild. The former will build the package for Python 3. The latter says that pybuild should wield it's magic as a buildsytem. For this to work we need to export the environment variable PYBUILD_NAME and set it's value to the name of the package you want to distribute.

Unfortunately not all that magic worked for me and I needed to configure the used system to distutils. This surprised me because as to my knowledge nowadays setuptools is the way to go. distutils and setuptools have been merged some time ago and maybe this is just some backwards compatibility.

In addition I disabled tests for this package because in my case they are done separately in a CI environment. Only after successful tests the Python package is eligible for distribution.

In the end the rules look like this:

#!/usr/bin/make -f

export PYBUILD_NAME=mylib
export PYBUILD_SYSTEM=distutils
export PYBUILD_DISABLE=test

%:
    dh $@ --with python3 --buildsystem=pybuild

The packaging process was quite nice and and I now enjoy being able to install my first Python 3-only package through apt!