Distributing Python in 2015

aka. oh setuptools, how I've underestimated you

18 Nov '15

When I got into Python, there were too many packaging tools: setuptools, distutils, distribute, distutils2, and bento. I’m sure I missed some. Luckily, only two survive: distutils and setuptools.

How to choose? Basically, if you follow best practice and use virtualenv, pip, or wheels, you want to use setuptools. This clarity is what Python needs. Also a big help: the setuptools documentation has also got substantially better.


But why use a packaging tool in the first place? An example is always good. Say you develop a tool, which you might want to give some colleagues eventually. You could just get everybody to clone the git repo. But setuptools helps solve several problems, so even if you never plan to upload the package to PyPI you definitely want to use setuptools.

I’ve chosen a sensible directory structure with a virtual environment:

project/
├── env
[...]
├── package
│   ├── __init__.py
│   ├── main.py
│   └── templates
│       └── default.jinja
└── setup.py

For this package, setup.py looks like this:

#!/usr/bin/env python

from setuptools import setup

setup(
    # [...] usual metadata: author, etc.
    packages=['package'],
    install_requires=['jinja'],
    package_data={
        'package': ['templates/*']
    },
    zip_safe=True,
    entry_points={
        'console_scripts': ['bin_name = package.main:main']
    }
)

package_data is interesting. In this case, I have some Jinja templates that I want my package to use. Usually, I’d get the path from __file__ and simply read the file. This doesn’t work so well in an egg or a wheel, because it’s zipped. I could set zip_safe to False, or I simply use pkg_resources which provides easy methods to get the contents. Much less of a hack!

entry_points solves a huge problem: How do you deploy a script that uses your package? setuptools makes this simple, just define an entry point (aka. a method) in the package, and the rest is taken care of - even on Windows. I can’t stress how useful this is.

Now, with the virtual environment activated, I just run

$ python setup.py develop

This gets you an executable in env/bin which calls the entry point. Once I’ve finished developing, to deploy the application, I simply make sure wheel is installed, create a wheel and distribute that:

$ pip install wheel
$ python setup.py bdist_wheel
$ cd dist/
$ python3 -m http.server
# python -m SimpleHTTPServer if you're using legacy Python

It’s really that simple. Yes, I know I’m late to the party. Thanks setuptools.

Python

Newer Older