Paket veröffentlichen

Schließlich könnt ihr das Paket auf dem Python Package Index (PyPI) oder einem anderen Index, z.B. GitLab Package Registry oder devpi, bereitstellen.

Für den Python Package Index müsst ihr euch bei Test PyPI registrieren. Test-PyPI ist eine separate Instanz, die zum Testen und Experimentieren vorgesehen ist. Um dort ein Konto einzurichten, geht ihr auf https://test.pypi.org/account/register/. Weitere Informationen findet ihr unter Using TestPyPI.

Nun könnt ihr eine ~/.config/pip/pip.conf-Datei erstellen:

[distutils]
index-servers=
    test

[test]
repository = https://test.pypi.org/legacy/
username = veit

Siehe auch

Wenn ihr die PyPI-Anmeldung automatisieren wollt, lest zunächst Careful With That PyPI.

Nachdem ihr registriert seid, könnt ihr euer Distribution Package mit uv publish hochladen.

Dabei könnt ihr uv publish entweder mit der Option --username __token__ verwenden oder die Umgebungsvariable UV_PUBLISH_USERNAME=__token__ setzen, um alle Archive unter /dist auf den Python Package Index hochladen:

$ uv publish --publish-url https://test.pypi.org/legacy/ --username __token__ dist/*
--publish-url

Die URL des Upload-Endpunkts (nicht die Index-URL).

--username

Den Benutzernamen für den Upload.

Bemerkung

Wenn ihr eine ähnliche Fehlermeldung erhaltet wie

The user 'veit' isn't allowed to upload to project 'example'

müsst ihr einen eindeutigen Namen für euer Paket auswählen:

  1. ändert das name-Argument in der pyproject.toml.-Datei

  2. entfernt das dist-Verzeichnis

  3. generiert die Archive neu

Überprüfen

Installation

Ihr könnt uv verwenden um euer Paket von Test PyPI zu installieren und zu überprüfen, ob es funktioniert:

uv add -i https://test.pypi.org/simple/ mypack

Bemerkung

Wenn ihr einen anderen Paketnamen verwendet habt als mypack, ersetzt ihn im obigen Befehl durch euren Paketnamen.

uv add sollte das Paket von Test PyPI installieren und die Ausgabe sollte in etwa so aussehen:

Resolved 8 packages in 5ms
Installed 7 packages in 36ms
 + mypack==0.1.0

Ihr könnt testen, ob euer Paket korrekt installiert wurde indem ihr main() aufruft:

$ uv run mypack
Hello from mypack!

Bemerkung

Die Pakete auf Test-PyPI werden nur temporär gespeichert. Wenn ihr ein Paket in den echten Python Package Index (PyPI) hochladen wollt, könnt ihr dies tun, indem ihr ein Konto auf pypi.org anlegt.

README

Überprüft auch, ob die README.rst-Datei auf der Test-PyPI-Seite korrekt angezeigt wird.

PyPI

Registriert euch nun beim Python Package Index (PyPI) und stellt sicher, dass die Zwei-Faktor-Authentifizierung.

Schließlich könnt ihr nun euer Paket auf PyPI veröffentlichen:

$ uv publish dist/*

Bemerkung

Ihr könnt Releases nicht einfach ersetzen da ihr Pakete mit derselben Versionsnummer nicht erneut hochladen könnt.

Bemerkung

Entfernt nicht alte Versionen aus dem Python Package Index. Dies verursacht nur Arbeit für jene, die diese Version weiter verwenden wollen und dann auf alte Versionen auf GitHub ausweichen müssen. PyPI hat eine yank-Funktion, die ihr stattdessen nutzen könnt. Dies ignoriert eine bestimmte Version, wenn sie nicht explizit mit == oder === angegeben wurde.

GitHub Action

Ihr könnt auch eine GitHub-Aktion erstellen, die ein Paket erstellt und auf PyPI hochlädt. Eine solche .github/workflows/pypi.yml-Datei könnte folgendermaßen aussehen:

.github/workflows/pypi.yml
 1name: Publish Python Package
 2
 3 on:
 4   release:
 5     types: [created]
 6
 7jobs:
 8  test:
 9    
10  package-and-deploy:
11    runs-on: ubuntu-latest
12    needs: [test]
13    steps:
14    - name: Checkout
15      uses: actions/checkout@v4
16      with:
17        fetch-depth: 0
18    - name: Set up Python
19      uses: actions/setup-python@v5
20      with:
21        python-version-file: .python-version
22        cache-dependency-path: '**/pyproject.toml'
23    - name: Setup cached uv
24      uses: hynek/setup-cached-uv@v2
25    - name: Create venv
26      run: |
27        uv venv
28        echo "$PWD/.venv/bin" >> $GITHUB_PATH
29    - name: Build
30      run: |
31        uv build
32    - name: Retrieve and publish
33      steps:
34      - name: Retrieve release distributions
35        uses: actions/download-artifact@v4
36      - name: Publish package distributions to PyPI
37        uses: pypa/gh-action-pypi-publish@release/v1
38        with:
39          username: __token__
40          password: ${{ secrets.PYPI_TOKEN }}
Zeilen 3–5

Dies stellt sicher, dass der Arbeitsablauf jedes Mal ausgeführt wird, wenn ein neues GitHub-Release für das Repository erstellt wird.

Zeile 12

Der Job wartet auf das Bestehen des test-Jobs bevor er ausgeführt wird.

Zeile 31

Hier sollte mypack durch euren Paketnamen ersetzt werden.

Zeile 36

Die GitHub-Aktion actions/download-artifact stellt die gebauten Verteilungspakete bereit.

Zeile 38–41

Die GitHub-Aktion pypa/gh-action-pypi-publish veröffentlicht die Pakete mit dem Upload-Token auf PyPI.

Trusted Publishers

Trusted Publishers ist ein Verfahren zum Veröffentlichen von Paketen auf dem PyPI. Es basiert auf OpenID Connect und erfordert weder Passwort noch Token. Dazu sind lediglich die folgenden Schritte erforderlich:

  1. Fügt einen Trusted Publishers auf PyPI hinzu

    Je nachdem, ob ihr ein neues Paket veröffentlichen oder ein bestehendes aktualisieren wollt, unterscheidet sich der Prozess geringfügig:

    • zum Aktualisieren eines bestehenden Pakets siehe Adding a trusted publisher to an existing PyPI project

    • zum veröffentlichen eines neuen Pakets gibt es ein spezielles Verfahren, Pending Publisher genannt; s.a. Creating a PyPI project with a trusted publisher

      Ihr könnt damit auch einen Paketnamen reservieren, bevor ihr die erste Version veröffentlicht. Damit könnt ihr sicherstellen, dass ihr das Paket auch unter dem gewünschten Namen veröffentlichen könnt.

      Hierfür müsst ihr in pypi.org/manage/account/publishing/ einen neuen Pending Publisher erstellen mit

      • Namen des PyPI-Projekts

      • GitHub-Repository Owner

      • Namen des Workflows, z.B. publish.yml

      • Name der Umgebung (optional), z.B. release

  2. Erstellt eine Umgebung für die GitHub-Actions

    Wenn wir eine Umgebung auf PyPI angegeben haben, müssen wir diese nun auch erstellen. Das kann in Settings ‣ Environments für das Repository geschehen. Der Name unserer Umgebung ist release.

  3. Konfiguriert den Arbeitsablauf

    Hierfür erstellen wir nun die Datei .github/workflows/publish.yml in unserem Repository:

    .github/workflows/pypi.yml
    10package-and-deploy:
    11    runs-on: ubuntu-latest
    12+   environment: release
    13+   permissions:
    14+     id-token: write
    15    needs: [test]
    16    steps:
    
    Zeile 12

    Die Angabe einer GitHub-Umgebung ist optional, wird aber dringend empfohlen.

    Zeilen 13–14

    Die write-Berechtigung ist für Trusted Publishing erforderlich.

    Zeilen 42–44

    username und password werden für die GitHub-Aktion pypa/gh-action-pypi-publish nicht mehr benötigt.

    .github/workflows/pypi.yml
    40   - name: Publish package distributions to PyPI
    41     uses: pypa/gh-action-pypi-publish@release/v1
    42-    with:
    43-      username: __token__
    44-      password: ${{ secrets.PYPI_TOKEN }}
    

Digital Attestations

Seit 14. November 2024 unterstützt PyPI auch PEP 740 mit Digital Attestations. PyPI verwendet das in-toto Attestation Framework zum Ausstellen der Digital Attestations SLSA Provenance und PyPI Publish Attestation (v1).

Die Erstellung und Veröffentlichung erfolgt standardmäßig, sofern über Trusted Publishing und die GitHub-Action pypa/gh-action-pypi-publish zum Veröffentlichen verwendet werden:

.github/workflows/pypi.yml
jobs:
  pypi-publish:
    name: Upload release to PyPI
    runs-on: ubuntu-latest
    environment:
      name: pypi
      url: https://pypi.org/p/{YOUR-PYPI-PROJECT-NAME}
    permissions:
      id-token: write
    steps:
    - name: Publish package distributions to PyPI
      uses: pypa/gh-action-pypi-publish@release/v1

Bemerkung

Die Unterstützung für die automatische Erstellung von Digital Attestations und die Veröffentlichung aus anderen Trusted Publisher-Umgebungen ist geplant.