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:
black
to lint codeisort
to sort importsflake8
to check PEP 8 compliancetrailing-newline
to flag files that do not end with a blank newline*mixed-line-ending
to use consistent CRLF or LF line endingsname-tests-test
to 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
,isort
andflake8
. - Runs
pytest
and generates a code coverage.xml
file usingpytest-cov
. - Builds the package documentation using
sphinx
and 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/major
accordingly (see Semantic Versioning). - Update
CITATION.cff
if 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 --tags
to 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 build
andpoetry publish -r test-pypi
to 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-url
argument is needed to install dependencies from PyPI. -
Once the package has been tested, run
poetry publish
to 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.