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
It’s really that simple. Yes, I know I’m late to the party. Thanks setuptools.