Packages

All our packages should be distributed on PyPI.

Use the Pypackage Cookiecutter template, and add the repository to pre-commit.ci:

pip install cookiecutter
cookiecutter gh:open-contracting/software-development-handbook --directory cookiecutter-pypackage

See also

Package-rated content in Directory layout, Testing and Linting

Metadata

If the package is distributed on PyPI, use this template for the setup.cfg file, adding options like entry_points and namespace_packages as needed, and removing the Jinja syntax if not using the Cookiecutter template:

[flake8]
max-line-length = 119
extend-ignore = E203

[metadata]
name = {{ cookiecutter.package_name }}
version = 0.0.1
author = Open Contracting Partnership
author_email = data@open-contracting.org
license = BSD
description = {{ cookiecutter.short_description }}
url = https://github.com/open-contracting/{{ cookiecutter.repository_name }}
long_description = file: README.rst
long_description_content_type = text/x-rst
classifiers =
    License :: OSI Approved :: BSD License
{%- if cookiecutter.os_independent == "y" %}
    Operating System :: OS Independent
{%- else %}
    Operating System :: POSIX :: Linux
{%- endif %}
    Programming Language :: Python :: 3.8
    Programming Language :: Python :: 3.9
    Programming Language :: Python :: 3.10
    Programming Language :: Python :: 3.11
    Programming Language :: Python :: 3.12
    Programming Language :: Python :: Implementation :: CPython
{%- if cookiecutter.pypy == "y" %}
    Programming Language :: Python :: Implementation :: PyPy
{%- endif %}

[options]
packages = find:
install_requires =

[options.packages.find]
exclude =
    tests
    tests.*

[options.extras_require]
test =
    coveralls
    pytest
    pytest-cov
docs =
    furo
    sphinx
    sphinx-autobuild

If the package isn’t distributed on PyPI, use this template setup.cfg:

[metadata]
name = NAME
version = 0.0.0
license = BSD

[options]
packages = find:
install_requires =
    REQUIREMENT

Reference: Packaging and distributing projects

Requirements

  • Use install_requires and extras_require in the setup.cfg file

  • Do not use a requirements.txt file

  • Sort requirements alphabetically

Classifiers

"Operating System :: OS Independent"

The package is tested on macOS, Windows and Ubuntu.

"Operating System :: POSIX :: Linux"

The package is tested on Ubuntu only.

"Programming Language :: Python :: Implementation :: PyPy"

The package is tested on PyPy.

Documentation

The template reads the documentation from a README.rst file. To convert a README.md file, install pandoc and run:

pandoc --from=markdown --to=rst --output=README.rst README.md

Release process

One-time setup

To publish tagged releases to PyPI, create a .github/workflows/pypi.yml file:

name: Publish to PyPI
on: push
jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: '3.10'
      - run: pip install --upgrade build
      - run: python -m build --sdist --wheel
      - name: Publish to TestPyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          repository-url: https://test.pypi.org/legacy/
          skip-existing: true
      - name: Publish to PyPI
        if: startsWith(github.ref, 'refs/tags')
        uses: pypa/gh-action-pypi-publish@release/v1

The open-contracting organization sets the PYPI_API_TOKEN organization secret to the API token of the opencontracting PyPI user, and TEST_PYPI_API_TOKEN to that of the TestPyPI user.

After publishing the first release to PyPI, add additional owners.

  1. Ensure that you are on an up-to-date main branch:

    git checkout main
    git pull --rebase
    
  2. Ensure that the package is ready for release:

    • All tests pass on continuous integration

    • The version number is correct in setup.cfg and docs/conf.py (if present)

    • The changelog is up-to-date and dated

  3. Tag the release, replacing x.y.z twice:

    git tag -a x.y.z -m 'x.y.z release.'
    
  4. Push the release:

    git push --follow-tags
    
  5. Announce on the discussion group if relevant

Reference: Publishing package distribution releases using GitHub Actions CI/CD workflows