Skip to main content

Python PyPI template library — companion to the .NET NuGetLibrary in this template repo.

Project description

PyPiLibrary

Python PyPI template — companion to the .NET NuGetLibrary in this repo. Published to PyPI as ptr727-projecttemplate-library.

Stack

  • Build backendhatchling via pyproject.toml
  • Env / deps / publishuv (Astral)
  • Lint + formatruff
  • Type checkerpyright
  • Testspytest
  • PublishPyPI Trusted Publishing via pypa/gh-action-pypi-publish (no API token in repo secrets)
  • VersionNerdbank.GitVersioning (NBGV) shared with the .NET side. CI replaces the __version__ line in _version.py (in place) with NBGV's AssemblyFileVersion (Major.Minor.Patch.BuildNumber, PEP 440 valid) before uv build; that matches the .NET assemblies' FileVersion stamp. .NET's AssemblyVersion (a separate NBGV output) and NuGet/Docker (NBGV SemVer2) carry different strings, but all four derive from the same NBGV computation per release commit.

Layout

PyPiLibrary/
    pyproject.toml
    README.md
    src/
        ptr727_projecttemplate_library/
            __init__.py
            _version.py
            example.py
    tests/
        __init__.py
        test_example.py

Local Development

The repo's devcontainer installs uv automatically and runs uv sync for this project on first open. To work outside the devcontainer:

# from the repo root
cd PyPiLibrary
uv sync                          # creates .venv, installs deps + dev group
uv run ruff check                # lint
uv run ruff format --check       # formatting check
uv run pyright                   # type check
uv run pytest                    # tests
uv build                         # wheel + sdist into ./dist

Publishing

Releases are produced by .github/workflows/build-pypilibrary-task.yml (called from build-release-task.yml to build, lint, type-check, test, and upload the wheel + sdist as a workflow-run artifact). Publishing is a separate top-level publish-pypi job in publish-release.yml that downloads the artifact by name and runs Trusted Publishing — no PYPI_API_TOKEN secret is involved. The publish job has id-token: write only at that single job level, so the test-pull-request flow (which calls the same build task during PR validation) doesn't need to propagate that permission through the reusable workflow chain.

First-time setup (one-time, on PyPI):

Prerequisite: enable 2FA on the PyPI account (TOTP or hardware key). PyPI requires it before any trusted publisher can be registered.

  1. PyPIAccount settingsPublishingAdd a new pending publisher (direct link). If the project already exists on PyPI, go to the project page → ManagePublishingAdd a new publisher instead — the "pending" form is only for projects that don't exist yet. Fields:
    • PyPI project name: ptr727-projecttemplate-library
    • Owner: ptr727
    • Repository name: ProjectTemplate
    • Workflow filename: publish-release.yml
    • Environment name: pypi
  2. GitHub repoSettingsEnvironmentsNew environmentpypi. The environment owns deploy-time guardrails:
    • Deployment branch ruleSelected branches and tags → add main. This step is mandatory — Trusted Publishing without a branch restriction is a documented security anti-pattern. Defense in depth: the publish-pypi job in .github/workflows/publish-release.yml also has if: github.ref == 'refs/heads/main' so develop pushes don't even attempt to enter the environment gate (they'd otherwise stall as blocked deployments). The if: is the operational gate; the env branch rule is the security boundary that holds even if the if: gets misconfigured.
    • (Optional) add yourself as a required reviewer so each publish requires a click — useful belt-and-suspenders against an accidental release.
  3. The first successful release converts the pending publisher to a real publisher. After that the same OIDC exchange validates against the real publisher on every release.

Troubleshooting:

  • invalid-publisher: ... Publisher with matching claims was not found — the publisher hasn't been registered yet, or one of the five claim fields (owner, repo, workflow filename, environment name, project name) doesn't match. Re-check step 1.
  • manifest unknown from docker: pulling ghcr.io/pypa/gh-action-pypi-publish — the SHA pinned in publish-release.yml doesn't correspond to a release tag with a published GHCR image. Pin to the SHA that the upstream tag (# vX.Y.Z comment) actually points at on pypa/gh-action-pypi-publish.

Fallback (API token instead of Trusted Publishing): drop the id-token: write permission from the publish-pypi job, add password: ${{ secrets.PYPI_API_TOKEN }} to the pypa/gh-action-pypi-publish step, and store the token as a repo secret. Also pass attestations: false since attestations require the OIDC token. The OIDC path is preferred — no long-lived secret in the repo — so use the token method only when Trusted Publishing isn't an option.

Template Adoption

When deriving a new project from this template:

  • Replace the package name ptr727-projecttemplate-library (in pyproject.toml, this README, and CI) with your name.

  • Rename src/ptr727_projecttemplate_library/ to your import name.

  • Re-register the trusted publisher on PyPI under the new project name.

  • Pick a versioning scheme. The template defaults to NBGV-driven versioning shared with the .NET side: _version.py holds __version__ = "0.0.0" as a local-development placeholder, and the CI step "Write version into _version.py step" in build-pypilibrary-task.yml replaces the __version__ line (in place, preserving the docstring) with NBGV's AssemblyFileVersion (always Major.Minor.Patch.BuildNumber, all numeric, PEP 440 valid) just before uv build. PyPI therefore ships the same version string that's stamped into the .NET assemblies as FileVersion. .NET's AssemblyVersion (the binary-compat identity — a separate NBGV output) and the NuGet package version / Docker tags (which use NBGV's SemVer2 — PEP 440 doesn't accept its prerelease / build-metadata suffixes) all carry different strings; but all four derive from the same NBGV computation against version.json + git history and correspond to the same release commit. If you want a different scheme, replace both _version.py and the workflow step. Two common alternatives:

    • hatch-vcs — derive the version from git tags. Add it to [build-system].requires and switch [tool.hatch.version] to source = "vcs". Drop the CI overwrite step. Pairs well with tag-driven releases and removes the NBGV dependency.
    • Manual bumps — edit _version.py in each release PR. Simplest, but easy to forget. Drop the CI overwrite step.

    The publish workflow uses skip-existing: true so a re-upload of the same version is a no-op instead of a failure — useful when iterating on releases without bumping NBGV.

If you don't want a Python project at all, delete the PyPiLibrary/ folder, the build-pypilibrary-task.yml workflow, the build-pypilibrary job in build-release-task.yml, the publish-pypi job in publish-release.yml, and the uv block in .github/dependabot.yml.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

File details

Details for the file ptr727_projecttemplate_library-1.0.47.53309.tar.gz.

File metadata

File hashes

Hashes for ptr727_projecttemplate_library-1.0.47.53309.tar.gz
Algorithm Hash digest
SHA256 cfb736c8da30b43b21b4303187d41843196828ab207994f87c4f0e63fc7106cb
MD5 a68b74a9ce4b81629026e96f8683e70f
BLAKE2b-256 c40578dfea55271c3f23e290739af3c337217fa26c91d344cff0161c0d95c50b

See more details on using hashes here.

Provenance

The following attestation bundles were made for ptr727_projecttemplate_library-1.0.47.53309.tar.gz:

Publisher: publish-release.yml on ptr727/ProjectTemplate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file ptr727_projecttemplate_library-1.0.47.53309-py3-none-any.whl.

File metadata

File hashes

Hashes for ptr727_projecttemplate_library-1.0.47.53309-py3-none-any.whl
Algorithm Hash digest
SHA256 e21debeffea1e55928255c64d581c18f29c1e17b2ce6b5761bf0e940514099e5
MD5 d03b919f011ac52dc6eedcc25bc3a694
BLAKE2b-256 6eba6ffff6252270546166e7a0ccbbd9cd933303b11bbfcf9d530aa7e13bc54d

See more details on using hashes here.

Provenance

The following attestation bundles were made for ptr727_projecttemplate_library-1.0.47.53309-py3-none-any.whl:

Publisher: publish-release.yml on ptr727/ProjectTemplate

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page