Python-pakketten bouwen: Handleiding van ontwikkeling tot PyPI

Master Python packaging van code tot PyPI-implementatie

Inhoud

Python packaging heeft zich aanzienlijk ontwikkeld, met moderne tools en standaarden die het makkelijker dan ooit maken om je code te distribueren.

Deze gids leidt je door het bouwen van professionele Python-pakketten en het publiceren ervan op PyPI. Als je nieuw bent in Python of een snelle naslag nodig hebt, bekijk dan onze Python Cheatsheet om op te schalen met de fundamentele aspecten van Python.

Python Packages

Waarom je Python-code moeten verpakken?

Het verpakken van je Python-project biedt meerdere voordelen:

  • Herbruikbaarheid: Deel code over meerdere projecten zonder kopiëren en plakken
  • Distributie: Laat anderen je code installeren met een eenvoudige pip install
  • Afhankelijkheden beheren: Specificeer en beheer afhankelijkheden duidelijk
  • Versiebeheer: Volg releases en behoud achterwaartse compatibiliteit
  • Professionele standaarden: Volg de beste praktijken van de Python-gemeenschap
  • Documentatie: Structuur bevordert goedkeurde documentatie en testen

Moderne Python-pakketstructuur

Een goed georganiseerd pakket volgt deze structuur:

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

De src-layout wordt nu voorkeur gegeven boven de platte layout omdat het:

  • Voorkomt onbedoelde imports van niet geïnstalleerde code tijdens de ontwikkeling
  • Maakt testen betrouwbaarder door installatie te vereisen
  • Slaat duidelijk de broncode apart van andere projectbestanden

Bij het organiseren van de interne structuur van je pakket, overweeg dan het toepassen van principes van clean architecture om je code meer onderhoudbaar en testbaar te maken. Onze gids over Python Design Patterns for Clean Architecture behandelt SOLID principes, dependency injection en laaggeoriënteerde architectuurpatronen die uitstekend werken met Python-pakketten.

Het pyproject.toml-bestand: Moderne configuratie

pyproject.toml (PEP 518, 621) is de moderne standaard voor Python-projectconfiguratie, die de oude setup.py vervangt:

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

[project]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Your Name", email = "you@example.com"}
]
keywords = ["example", "tutorial", "package"]
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"]

Belangrijke configuratiesecties

  1. build-system: Specificeert de build backend (setuptools, hatchling, poetry-core, flit-core)
  2. project: Kernmetadata en afhankelijkheden
  3. project.optional-dependencies: Extra functie-afhankelijkheden (dev, docs, testen)
  4. project.scripts: Command-line invoegpunten
  5. tool.*: Tool-specifieke configuratie (black, pytest, mypy, enz.)

Een build backend kiezen

Setuptools (Standaard keuze)

De meest gebruikte en compatibele optie:

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

Voordelen: Universele compatibiliteit, uitgebreide functies, grote ecosystem Nadelen: Meer gedetailleerde configuratie, trager dan nieuwere alternatieven

Hatchling (Moderne & snelle)

Lichtgewicht en prestatiester backend:

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

Voordelen: Snelle builds, eenvoudige configuratie, goede standaarden Nadelen: Minder plugins dan setuptools

Poetry (Alles-in-één)

Volledig afhankelijkheden- en omgevingsbeheer:

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

[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "A fantastic Python package"
authors = ["Your Name <you@example.com>"]

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

Voordelen: Afhankelijkheidsoplossing, lockbestanden, geïntegreerde workflow Nadelen: Verschillende workflow, groter tooloppervlak

Flit (Minimalistisch)

Eenvoudig hulpmiddel voor eenvoudige pakketten:

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

Voordelen: Uiterst eenvoudig, minimale configuratie Nadelen: Beperkte functies voor complexe pakketten

Je pakket bouwen

Installatie van build-tools

# Installeer het moderne build-hulpmiddel
pip install build

# Of voor Poetry
pip install poetry

# Of voor Hatchling
pip install hatch

Bouwen van distributiefbestanden

Gebruik het build-pakket (werkt met elke backend):

# Vorige builds opruimen
rm -rf dist/ build/ *.egg-info

# Bouw bronverdeling en wheel
python -m build

# Dit creëert:
# dist/mypackage-0.1.0.tar.gz    (bronverdeling)
# dist/mypackage-0.1.0-py3-none-any.whl  (wheel)

Gebruik Poetry:

poetry build

Gebruik Hatch:

hatch build

Begrip van distributieformaten

Bronverdeling (sdist) - .tar.gz

  • Bevat broncode en bouwinstructies
  • Gebruikers’ pip bouwt het tijdens de installatie
  • Bevat tests, documentatie en andere ontwikkelbestanden

Wheel - .whl

  • Vooraf gebouwde binaire distributie
  • Snel installeren (geen bouwstap)
  • Platformspecifiek of puur Python
  • Aanbevolen formaat voor distributie

Publiceren op PyPI

Methode 1: Handmatige upload met Twine (Traditioneel)

# Installeer twine
pip install twine

# Controleer pakket voor upload
twine check dist/*

# Upload eerst naar TestPyPI (aanbevolen)
twine upload --repository testpypi dist/*

# Test installatie vanuit TestPyPI
pip install --index-url https://test.pypi.org/simple/ mypackage

# Upload naar productie PyPI
twine upload dist/*

Configureer referenties 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: Vertrouwelijke publicatie (Aanbevolen voor CI/CD)

Vertrouwelijke publicatie gebruikt OpenID Connect (OIDC) om te verifiëren vanuit CI/CD-platforms zonder tokens op te slaan.

Instelling in PyPI:

  1. Ga naar je PyPI-projectinstellingen
  2. Navigeer naar de “Publicatie” sectie
  3. Voeg een nieuwe “verwachte uitgever” toe
  4. Configureer:
    • Eigenaar: Je GitHub-gebruikersnaam/organisatie
    • Repository: Repositorynaam
    • Workflow: publish.yml
    • Omgeving: release (optioneel maar aanbevolen)

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

Voordelen:

  • Geen API-tokens om te beheren of te beveiligen
  • Automatische verificatie via OIDC
  • Verhoogde beveiliging via omgevingsbeschermingsregels
  • Audittrail van alle publicaties

Beste praktijken

1. Versiebeheer

Gebruik semantisch versienummering (SemVer): MAJOR.MINOR.PATCH

# Installeer versieverhogingshulpmiddel
pip install bump2version

# Verhoog versie
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0

Configureer 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. Essentiële bestanden opnemen

README.md: Duidelijke projectbeschrijving, installatie, gebruikvoorbeelden LICENSE: Kies een geschikte licentie (MIT, Apache 2.0, GPL, enz.) CHANGELOG.md: Documenteer veranderingen tussen versies .gitignore: Uitsluiten van bouwproducten, caches, virtuele omgevingen

3. Uitgebreide testen

# Installeer testafhankelijkheden
pip install pytest pytest-cov

# Voer tests uit met coverage
pytest --cov=mypackage tests/

# Genereer coverage rapport
pytest --cov=mypackage --cov-report=html tests/

Configureer in pyproject.toml:

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

4. Codekwaliteits-tools

# Formatteren
black src/ tests/

# Linten
flake8 src/ tests/

# Typechecken
mypy src/

# Importen sorteren
isort src/ tests/

Integreer 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. Documentatie

Gebruik Sphinx voor documentatie:

# Installeer Sphinx
pip install sphinx sphinx-rtd-theme

# Initialiseer docs
cd docs
sphinx-quickstart

# Bouw documentatie
make html

Of gebruik MkDocs voor eenvoudige Markdown-gebaseerde documentatie:

pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve

6. Continue integratie

Het testen van Python-pakketten over verschillende platforms en omgevingen is cruciaal voor betrouwbaarheid. Voor inzichten in Python-prestaties in verschillende implementatiescenario’s, zie onze vergelijking van AWS Lambda performance: JavaScript vs Python vs Golang, die verkent hoe Python presteert in serverloze omgevingen.

Volledige 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: Set up Python
        uses: actions/setup-python@v4
        with:
          python-version: ${{ matrix.python-version }}
      
      - name: Install dependencies
        run: |
          python -m pip install --upgrade pip
          pip install -e ".[dev]"          
      
      - name: Run tests
        run: pytest --cov=mypackage --cov-report=xml
      
      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage.xml
  
  lint:
    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 dependencies
        run: |
          pip install black flake8 mypy          
      
      - name: Black formatter check
        run: black --check src/ tests/
      
      - name: Flake8
        run: flake8 src/ tests/
      
      - name: MyPy
        run: mypy src/

Vaak voorkomende problemen en oplossingen

Probleem 1: Importfouten na installatie

Probleem: Pakket geïnstalleerd maar imports falen

Oplossing: Zorg voor een correcte pakketstructuur met __init__.py bestanden en correcte pyproject.toml configuratie:

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

Probleem 2: Ontbrekende afhankelijkheden

Probleem: Pakket geïnstalleerd maar faalt tijdens runtime door ontbrekende afhankelijkheden

Oplossing: Declareer alle runtime-afhankelijkheden in pyproject.toml:

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

Probleem 3: Versieconflicten

Probleem: Pakket werkt in ontwikkeling maar faalt in productie

Oplossing: Gebruik virtuele omgevingen en specificeer minimumversies:

# Maak een geïsoleerde omgeving
python -m venv .venv
source .venv/bin/activate  # Op Windows: .venv\Scripts\activate

# Installeer in editable modus voor ontwikkeling
pip install -e ".[dev]"

Probleem 4: Grote pakketgrootte

Probleem: Pakket neemt te lang om te downloaden/installeren

Oplossing: Uitsluiten van onnodige bestanden met MANIFEST.in:

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 *

Probleem 5: Platformspecifieke problemen

Probleem: Pakket werkt op één OS maar faalt op een ander

Oplossing: Test op meerdere platforms met CI/CD matrix builds (zie CI-voorbeeld hierboven)

Publicatiechecklist

Voor het publiceren van je pakket op PyPI, controleer:

  • Alle tests passen over Python-versies
  • Code is opgemaakt en geverifieerd
  • README.md is compleet met voorbeelden
  • LICENSE-bestand is opgenomen
  • Versienummer is bijgewerkt
  • CHANGELOG.md is bijgewerkt
  • Afhankelijkheden zijn correct gespecificeerd
  • Pakket bouwt zonder fouten (python -m build)
  • Pakket getest op TestPyPI
  • Documentatie is up-to-date
  • Git-repository is gemarkeerd met versie
  • GitHub-release is aangemaakt (voor vertrouwelijke publicatie)

Geavanceerde onderwerpen

Invoegpunten en CLI-tools

Maak command-line interfaces:

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

Implementatie in src/mypackage/cli.py:

import click

@click.command()
@click.option('--name', default='World', help='Naam om te groeten')
def main(name):
    """Voorbeeld van een eenvoudige CLI-tool"""
    click.echo(f'Hello, {name}!')

if __name__ == '__main__':
    main()

Voor een echte wereldvoorbeeld van het maken van Python-pakketten die interactie hebben met externe APIs, bekijk dan onze gids over Integrating Ollama with Python, die het bouwen van Python-clients met zowel REST API als officiële bibliotheekintegraties demonstreert.

Plug-insysteem

Inschakelen van plug-inontdekking:

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

C-extensies

Voor prestatiekritieke code:

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

Namespace-pakketten

Voor gedistribueerde pakketten onder een gemeenschappelijke namespace:

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

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

Nuttige tools en bronnen

Pakketontwikkelingstools

  • cookiecutter: Projectsjablonen voor Python-pakketten
  • tox: Testautomatisering over Python-versies
  • nox: Flexibele testautomatisering (tox alternatief)
  • pre-commit: Git hook framework voor codekwaliteit
  • commitizen: Standaardiseren van commitberichten en versiebeheer

Documentatieplatforms

  • Read the Docs: Gratis documentatiehosting
  • GitHub Pages: Host MkDocs of Sphinx-documentatie
  • pdoc: Eenvoudige API-documentatiegenerator

Pakketregisters

  • PyPI: Het officiële Python-pakketindex (pypi.org)
  • TestPyPI: Testomgeving (test.pypi.org)
  • Anaconda.org: Conda-pakketdistributie
  • GitHub Packages: Privé-pakkethosting

Monitoring en analytics

  • PyPI Stats: Downloadstatistieken voor pakketten
  • Libraries.io: Afhankelijkheidsmonitoring en waarschuwingen
  • Snyk: Beveiligingsvulnerabiliteitsanalyse
  • Dependabot: Automatische afhankelijkheidsupdates

Conclusie

Moderne Python-pakketverpakking is ontwikkeld om voor ontwikkelaars vriendelijk te zijn met standaarden zoals pyproject.toml, krachtige build backends en beveiligde publicatie via Trusted Publishing. Door deze gids te volgen, kun je professionele, onderhoudbare Python-pakketten maken die je gebruikers effectief dienen.

Belangrijke conclusies:

  1. Gebruik pyproject.toml voor alle nieuwe projecten
  2. Kies de juiste build backend voor jouw behoeften
  3. Implementeer automatische testen en CI/CD
  4. Gebruik Trusted Publishing voor beveiligde, tokenvrije implementatie
  5. Volg semantische versienummering en onderhoud changelogs
  6. Bied uitgebreide documentatie en voorbeelden

Het Python-pakketecosysteem blijft verbeteren met betere tools, standaarden en beveiligingsfuncties. Blijf op de hoogte van Python Enhancement Proposals (PEPs) en gemeenschapsbest practices om je pakketten modern en onderhoudbaar te houden.

Andere nuttige artikelen op deze site