A cookiecutter template for a PyPI-ready Python package.
Poetry is used for build and dependency management, pytest for unit testing and sphinx for documentation. The template is based on Python Packages and was developed for my use so may need tweaking.
Creates a Python project with the following structure:
[package-name]
├── .github ┐
│ └── workflows │ CI workflow
│ └── build.yml ┘
├── .flake8 ┐
├── .gitignore | Tool configuration
├── .pre-commit-config.yaml ┘
├── docs ┐
│ ├── make.bat │
│ ├── Makefile |
│ └── source |
│ ├── api |
│ │ ├── [package-name].md |
│ │ ├── index.md |
│ │ └── utils.md |
│ ├── tutorials | Documentation
│ │ └── getting_started.md |
│ ├── conf.py |
│ ├── index.md |
│ └── README.md |
├── CHANGELOG.md │
├── CITATION.cff |
├── LICENSE |
├── README.md ┘
├── pyproject.toml ┐
├── src │
│ └── [package-name] │ Package code and
│ ├── __init__.py │ build configuration
│ ├── [package-name].py │
│ └── utils.py ┘
└── tests ┐ Unit
└── test_[package-name].py ┘ tests
Add your code to the src/[package-name]/ directory. All objects in [package-name].py are imported as [package-name].[object] and utils.py is an example utility function module.
The project uses Python 3.8. This can be changed by entering a different version when initialising the project and by updating the black and isort sections of pyproject.toml plus the CI matrix and CD environment variable in .github/workflows/build.yml.
The following are run as pre-commit hooks:
blackto lint codeisortto sort importsflake8to check PEP 8 compliancetrailing-newlineto flag files that do not end with a blank newline*mixed-line-endingto use consistent CRLF or LF line endingsname-tests-testto ensure the filenames of test scripts start "test"
Run pre-commit install to set these up.
* Update args in .pre-commit-config.yaml to ignore specific file extensions. See the documentation.
Uses Github Actions for continuous integration and deployment. The workflow:
- Checks code formatting with
black,isortandflake8. - Runs
pytestand generates a code coverage.xmlfile usingpytest-cov. - Builds the package documentation using
sphinxand checks any code examples withdoctest. - Generates a code coverage badge using
genbadge.
If a new version has been tagged:
- Pushes the documentation to GitHub Pages using the peaceiris/actions-gh-pages@v3.7.3 action.
- Creates a release if a tag is pushed using the git-release action. This generates a new DOI if the repository has been linked to your Zenodo account.
- Publishes the package to PyPI using the pypa/gh-action-pypi-publish@v1.5.0 action.
You may need to update the default permissions for GitHub Actions to "Read and write permissions" under Repository -> Settings -> Actions -> General -> Workflow permissions.
- Update
CHANGELOG.md. - Update the version number in
pyproject.toml. This can be automated by runningpoetry version patch/minor/majoraccordingly (see Semantic Versioning). - Update
CITATION.cffif your reference style includes the version number (see below). - Commit the changes and push to GitHub.
- Tag the release with the same version using
git tag [version]. - Push the tag using
git push --tagsto trigger the release.
Create an account at PyPI, generate an API token and add this to the GitHub repository as PYPI_API_TOKEN. The GitHub Actions workflow will publish the package when a new release is tagged.
For the first release, manually publish a package using Poetry:
-
To test the package, run
poetry buildandpoetry publish -r test-pypito publish the package to TestPyPi (you will need to set up a separate account). You may need to first runpoetry config repositories.test-pypi https://test.pypi.org/legacy/to add the TestPyPI repository. -
Check the package can be installed with
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple [package-name]. The--extra-index-urlargument is needed to install dependencies from PyPI. -
Once the package has been tested, run
poetry publishto publish to PyPI.
Create an account at Zenodo and link your GitHub account. Enable the package repository in Account -> GitHub and a DOI will be generated following each release. Once a DOI is available, README.md and CITATION.cff should be updated accordingly.
This approach is not ideal as the DOI is assigned after creating a release. This can be managed in part by using the Concept DOI which always points to the most recent version of the package. See the Zenodo FAQ for more information.
If you use the Concept DOI in CITATION.cff I suggest you do not include the package version.
The following are good resources for Python package development:
Released under the MIT license.
The template project also uses the MIT License. This can be changed by entering a different license when initialising a project and updating the LICENSE file accordingly.