Python Packaging Best Practices
Slightly opinionated, but nevertheless informed (imho) list of Python Packaging Best Practices, based on copious amounts of experience with many diverse Python projects, as a user and a packager.
This list seeks to be “tool” (solution) agnostic where possible, and describe things in terms of needs and outcomes, with the driving motivations being:
- PEP20 relevant (obvious, explicit, etc)
- Reducing friction, saving (everyone) time.
- Making your software easy to consume
- Encouraging contributions to your software
- Transcending the “downstream vs upstream divide”
We'll flesh out the motivations and caveats later.
Note: setup.py means setup.py or setup.cfg or other equivalent one place that has your package metadata.
Cheatsheet
General
- Have a clear LICENSE
- Pick a well-defined existing LICENSE.
- Have a LICENSE file in your repository
- Include LICENSE file to the source distribution (sdist). Use
MANIFEST.inorsetup.py:data_files - Define LICENSE in
setup.py:license
- Clearly declare exact Python version support.
setup.py:python_requires. This should at least match your CI coverage.
Dependencies
- Declare all dependencies in one place, in one way. This includes optional (
setup.py:extras_require) and test dependencies (setup.py:tests_require) - Put development dependencies in
extras_require[dev], nottests_require. They are not compulsory.- Understand the distinctions between development and user testing (eg: the latter doesn't want/need coverage, pylint, pep8 etc).
- Use
>=version specs for distribution dependencies.- Understand the distinctions between packaging and deployment (pinned) dependencies.
- Use these for development testing too. This makes sure issues are picked up in development early, and before releases/users.
- Use the lowest supported version of a dependency that your code requires.
Tests
- Have a HOW TO RUN TESTS section in your README in the repository root. Noone should need to go looking.
- Have one entry point, and ideally one command to run tests.
- Include tests, test data files, and test related files in the source distribution. Use
MANIFEST.inand/orsetup.py:packages - Tests that need some external thing (package, file, environment) SKIP not FAIL if that test requirement is not fulfilled.
- Run tests using the the source distribution. This gets you coverage of the user test scenario and tests the complete packaging pipeline.