Python-Pakete erstellen: Leitfaden von der Entwicklung bis zu PyPI

Meistern Sie Python-Paketierung von Code bis zur PyPI-Veröffentlichung

Inhaltsverzeichnis

Python-Paketierung hat sich erheblich weiterentwickelt, mit modernen Tools und Standards, die es einfacher denn je machen, Ihren Code zu verteilen.

Dieser Leitfaden führt Sie durch den Aufbau professioneller Python-Pakete und deren Veröffentlichung auf PyPI. Wenn Sie neu in Python sind oder eine schnelle Referenz benötigen, werfen Sie einen Blick auf unseren Python-Cheat-Sheet, um sich mit den Grundlagen von Python vertraut zu machen.

Python-Pakete

Warum Ihr Python-Code paketieren?

Das Paketieren Ihres Python-Projekts bietet mehrere Vorteile:

  • Wiederverwendbarkeit: Teilen Sie Code über mehrere Projekte hinweg ohne Kopieren und Einfügen
  • Verteilung: Ermöglichen Sie anderen, Ihren Code mit einem einfachen pip install zu installieren
  • Abhängigkeitsmanagement: Klare Spezifikation und Verwaltung von Abhängigkeiten
  • Versionierung: Verfolgen Sie Veröffentlichungen und halten Sie die Abwärtskompatibilität aufrecht
  • Professionelle Standards: Folgen Sie den Best Practices der Python-Community
  • Dokumentation: Die Struktur fördert eine ordnungsgemäße Dokumentation und Tests

Moderne Python-Paketstruktur

Ein gut organisiertes Paket folgt dieser Struktur:

my-package/
├── src/
│   └── mypackage/
│       ├── __init__.py
│       ├── core.py
│       └── utils.py
├── tests/
│   ├── __init__.py
│   └── test_core.py
├── docs/
│   └── index.md
├── .github/
│   └── workflows/
│       └── publish.yml
├── pyproject.toml
├── README.md
├── LICENSE
└── .gitignore

Die src-Layout wird jetzt der flachen Layout bevorzugt, weil sie:

  • Verhindert versehentliche Importe von nicht installiertem Code während der Entwicklung
  • Macht Tests zuverlässiger, indem die Installation erzwungen wird
  • Trennt klar den Quellcode von anderen Projektdateien

Beim Organisieren der internen Struktur Ihres Pakets sollten Sie die Anwendung von Clean-Architecture-Prinzipien in Betracht ziehen, um Ihren Code wartbarer und testbarer zu machen. Unser Leitfaden zu Python-Design Patterns für Clean Architecture behandelt SOLID-Prinzipien, Dependency Injection und geschichtete Architekturmuster, die hervorragend mit Python-Paketen funktionieren.

Die pyproject.toml-Datei: Moderne Konfiguration

pyproject.toml (PEP 518, 621) ist der moderne Standard für die Python-Projektkonfiguration und ersetzt das veraltete setup.py:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

[project]
name = "mypackage"
version = "0.1.0"
description = "Ein fantastisches Python-Paket"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Ihr Name", email = "you@example.com"}
]
keywords = ["beispiel", "tutorial", "paket"]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "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",
]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

[project.optional-dependencies]
dev = [
    "pytest>=7.4.0",
    "black>=23.0.0",
    "flake8>=6.0.0",
    "mypy>=1.5.0",
]
docs = [
    "sphinx>=7.0.0",
    "sphinx-rtd-theme>=1.3.0",
]

[project.urls]
Homepage = "https://github.com/yourusername/mypackage"
Documentation = "https://mypackage.readthedocs.io"
Repository = "https://github.com/yourusername/mypackage"
Issues = "https://github.com/yourusername/mypackage/issues"

[project.scripts]
mypackage-cli = "mypackage.cli:main"

[tool.setuptools.packages.find]
where = ["src"]

Wichtige Konfigurationsabschnitte

  1. build-system: Gibt das Build-Backend an (setuptools, hatchling, poetry-core, flit-core)
  2. project: Kernmetadaten und Abhängigkeiten
  3. project.optional-dependencies: Zusätzliche Feature-Abhängigkeiten (dev, docs, testing)
  4. project.scripts: Command-Line-Einstiegspunkte
  5. tool.*: Tool-spezifische Konfiguration (black, pytest, mypy, etc.)

Auswahl eines Build-Backends

Setuptools (Standardwahl)

Die am weitesten verbreitete und kompatible Option:

[build-system]
requires = ["setuptools>=68.0", "wheel"]
build-backend = "setuptools.build_meta"

Vorteile: Universelle Kompatibilität, umfangreiche Funktionen, großes Ökosystem Nachteile: Mehrere Konfiguration, langsamer als neuere Alternativen

Hatchling (Modern & Schnell)

Leichtgewichtiges und leistungsfähiges modernes Backend:

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

Vorteile: Schnelle Builds, einfache Konfiguration, gute Standardeinstellungen Nachteile: Weniger Plugins als setuptools

Poetry (All-in-One)

Vollständiges Abhängigkeits- und Umgebungsmanagement:

[build-system]
requires = ["poetry-core>=1.0.0"]
build-backend = "poetry.core.masonry.api"

[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "Ein fantastisches Python-Paket"
authors = ["Ihr Name <you@example.com>"]

[tool.poetry.dependencies]
python = "^3.8"
requests = "^2.28.0"

Vorteile: Abhängigkeitsauflösung, Lock-Dateien, integrierter Workflow Nachteile: Unterschiedlicher Workflow, größere Tool-Oberfläche

Flit (Minimalistisch)

Einfaches Tool für einfache Pakete:

[build-system]
requires = ["flit_core >=3.2,<4"]
build-backend = "flit_core.buildapi"

Vorteile: Extrem einfach, minimale Konfiguration Nachteile: Begrenzte Funktionen für komplexe Pakete

Bauen Ihres Pakets

Installieren Sie Build-Tools

# Installieren Sie das moderne Build-Tool
pip install build

# Oder für Poetry
pip install poetry

# Oder für Hatchling
pip install hatch

Erstellen Sie Verteilungsdateien

Mit dem build-Paket (funktioniert mit jedem Backend):

# Löschen Sie vorherige Builds
rm -rf dist/ build/ *.egg-info

# Bauen Sie die Quellverteilung und das Wheel
python -m build

# Dies erstellt:
# dist/mypackage-0.1.0.tar.gz    (Quellverteilung)
# dist/mypackage-0.1.0-py3-none-any.whl  (Wheel)

Mit Poetry:

poetry build

Mit Hatch:

hatch build

Verständnis der Verteilungsformate

Quellverteilung (sdist) - .tar.gz

  • Enthält Quellcode und Build-Anweisungen
  • Der pip des Benutzers baut es während der Installation
  • Enthält Tests, Dokumente und andere Entwicklungsdateien

Wheel - .whl

  • Vorinstallierte Binärverteilung
  • Schnelle Installation (kein Build-Schritt)
  • Plattformspezifisch oder plattformunabhängig
  • Empfohlenes Format für die Verteilung

Veröffentlichung auf PyPI

Methode 1: Manuelles Hochladen mit Twine (Traditionell)

# Installieren Sie twine
pip install twine

# Überprüfen Sie das Paket vor dem Hochladen
twine check dist/*

# Laden Sie zunächst auf TestPyPI hoch (empfohlen)
twine upload --repository testpypi dist/*

# Testen Sie die Installation von TestPyPI
pip install --index-url https://test.pypi.org/simple/ mypackage

# Laden Sie auf das Produktions-PyPI hoch
twine upload dist/*

Konfigurieren Sie die Anmeldeinformationen in ~/.pypirc:

[distutils]
index-servers =
    pypi
    testpypi

[pypi]
username = __token__
password = pypi-AgEIcHlwaS5vcmc...

[testpypi]
repository = https://test.pypi.org/legacy/
username = __token__
password = pypi-AgENdGVzdC5weXBpLm9yZw...

Methode 2: Vertrauenswürdige Veröffentlichung (Empfohlen für CI/CD)

Vertrauenswürdige Veröffentlichung verwendet OpenID Connect (OIDC) zur Authentifizierung von CI/CD-Plattformen ohne Speicherung von Tokens.

Setup in PyPI:

  1. Gehen Sie zu den Einstellungen Ihres PyPI-Projekts
  2. Navigieren Sie zum Abschnitt “Publishing”
  3. Fügen Sie einen neuen “pending publisher” hinzu
  4. Konfigurieren Sie:
    • Owner: Ihr GitHub-Benutzername/Organisation
    • Repository: Repository-Name
    • Workflow: publish.yml
    • Environment: release (optional, aber empfohlen)

GitHub Actions Workflow (.github/workflows/publish.yml):

name: Publish to PyPI

on:
  release:
    types: [published]

permissions:
  contents: read

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Install build dependencies
        run: |
          python -m pip install --upgrade pip
          pip install build          

      - name: Build package
        run: python -m build

      - name: Store distribution packages
        uses: actions/upload-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

  publish:
    needs: build
    runs-on: ubuntu-latest
    environment: release
    permissions:
      id-token: write

    steps:
      - name: Download distributions
        uses: actions/download-artifact@v3
        with:
          name: python-package-distributions
          path: dist/

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1

Vorteile:

  • Keine API-Tokens zu verwalten oder zu sichern
  • Automatische Authentifizierung über OIDC
  • Erhöhte Sicherheit durch Umgebungs-Schutzregeln
  • Audit-Trail aller Veröffentlichungen

Beste Praktiken

1. Versionsmanagement

Verwenden Sie semantisches Versionieren (SemVer): MAJOR.MINOR.PATCH

# Installieren Sie das Versionserhöhungstool
pip install bump2version

# Version erhöhen
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0

Konfigurieren Sie in .bumpversion.cfg:

[bumpversion]
current_version = 0.1.0
commit = True
tag = True

[bumpversion:file:pyproject.toml]
search = version = "{current_version}"
replace = version = "{new_version}"

[bumpversion:file:src/mypackage/__init__.py]
search = __version__ = "{current_version}"
replace = __version__ = "{new_version}"

2. Wichtige Dateien einbeziehen

README.md: Klare Projektbeschreibung, Installationsanleitung, Verwendungsbeispiele LICENSE: Wählen Sie eine geeignete Lizenz (MIT, Apache 2.0, GPL usw.) CHANGELOG.md: Dokumentieren Sie Änderungen zwischen den Versionen .gitignore: Ausschluss von Build-Artifacts, Caches, virtuellen Umgebungen

3. Umfassendes Testen

# Installieren Sie Testabhängigkeiten
pip install pytest pytest-cov

# Führen Sie Tests mit Abdeckung aus
pytest --cov=mypackage tests/

# Generieren Sie einen Abdeckungsbericht
pytest --cov=mypackage --cov-report=html tests/

Konfigurieren Sie in pyproject.toml:

[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_functions = ["test_*"]
addopts = "--strict-markers --cov=mypackage"

4. Code-Qualitätstools

# Formatierung
black src/ tests/

# Linting
flake8 src/ tests/

# Typüberprüfung
mypy src/

# Import-Sortierung
isort src/ tests/

Integrieren Sie in Pre-Commit-Hooks (.pre-commit-config.yaml):

repos:
  - repo: https://github.com/psf/black
    rev: 23.9.1
    hooks:
      - id: black

  - repo: https://github.com/pycqa/flake8
    rev: 6.1.0
    hooks:
      - id: flake8

  - repo: https://github.com/pre-commit/mirrors-mypy
    rev: v1.5.1
    hooks:
      - id: mypy

5. Dokumentation

Verwenden Sie Sphinx für die Dokumentation:

# Installieren Sie Sphinx
pip install sphinx sphinx-rtd-theme

# Initialisieren Sie die Dokumentation
cd docs
sphinx-quickstart

# Bauen Sie die Dokumentation
make html

Oder verwenden Sie MkDocs für einfachere Markdown-basierte Dokumentation:

pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve

6. Continuous Integration

Das Testen Ihrer Python-Pakete auf verschiedenen Plattformen und in verschiedenen Umgebungen ist entscheidend für die Zuverlässigkeit. Für Einblicke in die Python-Leistung in verschiedenen Bereitstellungsszenarien sehen Sie sich unseren Vergleich der AWS Lambda-Leistung: JavaScript vs Python vs Golang, der untersucht, wie sich Python in serverlosen Umgebungen verhält.

Vollständiger CI/CD-Workflow (.github/workflows/ci.yml):

name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ${{ matrix.os }}
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        python-version: ['3.8', '3.9', '3.10', '3.11', '3.12']

    steps:
      - uses: actions/checkout@v4

      - name: Python einrichten
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}

      - name: Abhängigkeiten installieren
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"          

      - name: Tests ausführen
        run: pytest --cov=mypackage --cov-report=xml

      - name: Abdeckung hochladen
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml

  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Python einrichten
        uses: actions/setup-python@v4
        with:
          python-version: '3.11'

      - name: Abhängigkeiten installieren
        run: |
          pip install black flake8 mypy          

      - name: Black-Formatierungsprüfung
        run: black --check src/ tests/

      - name: Flake8
        run: flake8 src/ tests/

      - name: MyPy
        run: mypy src/

Häufige Fallstricke und Lösungen

Problem 1: Importfehler nach der Installation

Problem: Paket installiert, aber Importe scheitern

Lösung: Stellen Sie eine korrekte Paketstruktur mit __init__.py-Dateien und eine korrekte pyproject.toml-Konfiguration sicher:

[tool.setuptools.packages.find]
where = ["src"]
include = ["mypackage*"]

Problem 2: Fehlende Abhängigkeiten

Problem: Paket installiert, aber Laufzeitfehler aufgrund fehlender Abhängigkeiten

Lösung: Geben Sie alle Laufzeitabhängigkeiten in pyproject.toml an:

[project]
dependencies = [
    "requests>=2.28.0",
    "click>=8.1.0",
]

Problem 3: Versionskonflikte

Problem: Paket funktioniert in der Entwicklung, aber nicht in der Produktion

Lösung: Verwenden Sie virtuelle Umgebungen und geben Sie Mindestversionen an:

# Erstellen Sie eine isolierte Umgebung
python -m venv .venv
source .venv/bin/activate  # Auf Windows: .venv\Scripts\activate

# Installieren Sie im Entwicklermodus
pip install -e ".[dev]"

Problem 4: Große Paketgröße

Problem: Paket benötigt zu lange zum Herunterladen/Installieren

Lösung: Schließen Sie unnötige Dateien mit MANIFEST.in aus:

include README.md
include LICENSE
include pyproject.toml
recursive-include src *.py
recursive-exclude * __pycache__
recursive-exclude * *.py[co]
recursive-exclude tests *
recursive-exclude docs *

Problem 5: Plattformspezifische Probleme

Problem: Paket funktioniert auf einem Betriebssystem, aber nicht auf einem anderen

Lösung: Testen Sie auf mehreren Plattformen mit CI/CD-Matrix-Builds (siehe CI-Beispiel oben)

Veröffentlichungs-Checkliste

Bevor Sie Ihr Paket auf PyPI veröffentlichen, überprüfen Sie:

  • Alle Tests laufen auf verschiedenen Python-Versionen
  • Code ist formatiert und gelint
  • README.md ist vollständig mit Beispielen
  • LICENSE-Datei ist enthalten
  • Versionsnummer ist aktualisiert
  • CHANGELOG.md ist aktualisiert
  • Abhängigkeiten sind korrekt angegeben
  • Paket baut ohne Fehler (python -m build)
  • Paket auf TestPyPI getestet
  • Dokumentation ist aktuell
  • Git-Repository mit Version getaggt
  • GitHub-Release erstellt (für vertrauenswürdige Veröffentlichung)

Fortgeschrittene Themen

Einstiegspunkte und CLI-Tools

Erstellen Sie Befehlszeilenschnittstellen:

[project.scripts]
mypackage = "mypackage.cli:main"

Implementierung in src/mypackage/cli.py:

import click

@click.command()
@click.option('--name', default='World', help='Name zum Begrüßen')
def main(name):
    """Beispiel für ein einfaches CLI-Tool"""
    click.echo(f'Hallo, {name}!')

if __name__ == '__main__':
    main()

Für ein Praxisbeispiel zur Erstellung von Python-Paketen, die mit externen APIs interagieren, sehen Sie sich unsere Anleitung zur Integration von Ollama mit Python, die die Erstellung von Python-Clients mit sowohl REST-API als auch offiziellen Bibliotheksintegrationen demonstriert.

Plugin-Systeme

Aktivieren Sie die Plugin-Erkennung:

[project.entry-points."mypackage.plugins"]
plugin_a = "mypackage_plugin_a:PluginA"

C-Erweiterungen

Für leistungskritischen Code:

[tool.setuptools.ext-modules]
name = "mypackage._speedups"
sources = ["src/mypackage/_speedups.c"]

Namensraum-Pakete

Für verteilte Pakete unter einem gemeinsamen Namensraum:

[tool.setuptools.packages.find]
where = ["src"]
include = ["company.*"]

[tool.setuptools.package-data]
"company.mypackage" = ["py.typed"]

Nützliche Tools und Ressourcen

Paketentwicklungs-Tools

  • cookiecutter: Projektvorlagen für Python-Pakete
  • tox: Testautomatisierung über Python-Versionen
  • nox: Flexible Testautomatisierung (tox-Alternative)
  • pre-commit: Git-Hook-Framework für Code-Qualität
  • commitizen: Standardisieren Sie Commit-Nachrichten und Versionierung

Dokumentationsplattformen

  • Read the Docs: Kostenlose Dokumentationshosting
  • GitHub Pages: Hosten Sie MkDocs- oder Sphinx-Dokumentation
  • pdoc: Einfacher API-Dokumentationsgenerator

Paketregistries

  • PyPI: Der offizielle Python Package Index (pypi.org)
  • TestPyPI: Testumgebung (test.pypi.org)
  • Anaconda.org: Conda-Paketverteilung
  • GitHub Packages: Private Pakethosting

Überwachung und Analysen

  • PyPI Stats: Download-Statistiken für Pakete
  • Libraries.io: Abhängigkeitsüberwachung und -warnungen
  • Snyk: Sicherheitslücken-Scanning
  • Dependabot: Automatisierte Abhängigkeitsaktualisierungen

Fazit

Moderne Python-Paketierung hat sich zu einer developerfreundlichen Lösung mit Standards wie pyproject.toml, leistungsfähigen Build-Backends und sicherer Veröffentlichung über Trusted Publishing entwickelt. Mit dieser Anleitung können Sie professionelle, wartbare Python-Pakete erstellen, die Ihre Benutzer effektiv bedienen.

Wichtige Erkenntnisse:

  1. Verwenden Sie pyproject.toml für alle neuen Projekte
  2. Wählen Sie das richtige Build-Backend für Ihre Bedürfnisse
  3. Implementieren Sie automatisiertes Testen und CI/CD
  4. Verwenden Sie Trusted Publishing für sichere, tokenfreie Bereitstellung
  5. Folgen Sie semantischem Versionieren und pflegen Sie Changelogs
  6. Bieten Sie umfassende Dokumentation und Beispiele

Das Python-Paketierungsumfeld verbessert sich kontinuierlich mit besserer Tooling, Standards und Sicherheitsmerkmalen. Bleiben Sie mit Python Enhancement Proposals (PEPs) und Community-Best Practices auf dem Laufenden, um Ihre Pakete modern und wartbar zu halten.

Weitere nützliche Artikel auf dieser Seite