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.in or setup.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], not tests_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.in and/or setup.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.