Distutils is Python’s built-in mechanism for packaging and installing Python modules. It is very convenient for packaging up your source code, scripts and other files and creating a distribution to be uploaded to pypi as I’ve mentioned before. Distutils was discussed (pdf) at PyCon last year and it looks like there are efforts afoot to improve it to add some much needed features like unittesting and metadata. Add-on packages like pip add additional features like uninstallation and dependency management but nothing guarantees that your users have it. Although Python’s packaging and distribution model beats PHP’s hands down, there is still a lot of room for improvement to make it seamless.
Release management
In essence, these issues and enhancements boil down to making release management easier. When releasing your package, you want to make sure that it contains all the appropriate files, is tested and can be installed easily. Distutils helps with the installation, pip with the dependencies and virtualenv (a topic for a later post) helps a lot with testing package interactions. But what about unittests? What about cleaning up after setup.py? What about generating documentation or other files?
Extending distutils
Until all these features get put into distutils, you have to extend it yourself in setup.py. Fortunately, this is not very complicated and can buy you some reliability in your build process. Adding a command like python setup.py test is pretty trivial:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
from distutils.core import setup, Command from unittest import TextTestRunner, TestLoader import mymodule.tests cmdclasses = dict() class TestCommand(Command): """Runs the unit tests for mymodule""" user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): loader = TestLoader() t = TextTestRunner() t.run(loader.loadTestsFromModule(mymodule.tests)) # 'test' is the parameter as it gets added to setup.py cmdclasses['test'] = TestCommand setup(cmdclass = cmdclasses #... ) |
The same sort of functionality could be used to verify any prerequisites not already checked by distutils or pip, generate documentation without external dependencies like Make (although Django supports Python 2.3 before this functionality was available) or to create a uniform way to take source control diffs and submit patches. Executing these commands from one place makes the whole process more consistent and easy to understood. Hopefully the new enhancements to distutils will make the process even better.
I believe ‘from distutils.core import setup’ should be ‘from distutils.core import setup, Command’
@Tim
Fixed.
I’m not sure I agree with everything I wrote two and a half years ago. Extra commands in setup.py — when used wisely — can be good. However, there is something to be said for a simple setup.py too. As for automating testing, setuptools has an option (test_suite) for it. Setuptools also allows you to specify dependencies needed for testing as well with the tests_require option. If you create your own testing command by extending distutils then you may run into some issues if your module has dependencies that the tests require.