Tworzenie pakietów Pythona: przewodnik od rozwoju do PyPI

Zdobądź wiedzę na temat pakowania w Pythonie od kodu po wdrożenie na PyPI

Page content

Opakowanie Pythona uległo znacznym zmianom, a nowoczesne narzędzia i standardy sprawiają, że dystrybucja Twojego kodu jest łatwiejsza niż kiedykolwiek.

Ten przewodnik pokaże Ci, jak tworzyć profesjonalne pakiety Pythona i publikować je w PyPI. Jeśli jesteś nowy w Pythonie lub potrzebujesz szybkiego odniesienia, sprawdź nasz Python Cheatsheet, aby szybko nauczyć się podstaw Pythona.

Pakiety Pythona

Dlaczego opakowywać swój kod Pythona?

Opakowanie projektu Pythona oferuje wiele korzyści:

  • Odtwarzalność: Udostępniaj kod między wieloma projektami bez kopiowania i wklejania
  • Dystrybucja: Pozwól innym zainstalować Twój kod za pomocą prostego pip install
  • Zarządzanie zależnościami: Jasno określ i zarządzaj zależnościami
  • Wersjonowanie: Śledź wersje i utrzymuj kompatybilność wsteczną
  • Standardy profesjonalne: Obserwuj najlepsze praktyki społeczności Pythona
  • Dokumentacja: Struktura zachęca do odpowiedniej dokumentacji i testowania

Nowoczesna struktura pakietu Pythona

Dobrze zorganizowany pakiet powinien遵循 tej struktury:

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

src-layout jest teraz preferowany nad płaską strukturą, ponieważ:

  • Zabiega o zapobieganie przypadkowemu importowi kodu niezainstalowanego podczas rozwoju
  • Ułatwia testowanie wymuszając instalację
  • Jasno oddziela kod źródłowy od innych plików projektu

Podczas organizowania wewnętrznnej struktury pakietu warto rozważyć zastosowanie zasad czystej architektury, aby zwiększyć utrzymanie i testowalność kodu. Nasz przewodnik Wzorce projektowe Pythona dla czystej architektury pokazuje zasady SOLID, iniekcję zależności i wzorce architektury warstwowej, które świetnie działają z pakietami Pythona.

Plik pyproject.toml: nowoczesna konfiguracja

pyproject.toml (PEP 518, 621) jest nowoczesnym standardem konfiguracji projektu Pythona, zastępując starszy setup.py:

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

[project]
name = "mypackage"
version = "0.1.0"
description = "Świetny pakiet Pythona"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "MIT"}
authors = [
    {name = "Twoje Imię", email = "you@example.com"}
]
keywords = ["przykład", "tutorial", "package"]
classifiers = [
    "Status rozwoju :: 4 - Beta",
    "Odbiorca :: Programiści",
    "Licencja :: Zatwierdzona przez OSI :: Licencja MIT",
    "Język programowania :: Python :: 3",
    "Język programowania :: Python :: 3.8",
    "Język programowania :: Python :: 3.9",
    "Język programowania :: Python :: 3.10",
    "Język programowania :: Python :: 3.11",
    "Język programowania :: 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"]

Kluczowe sekcje konfiguracji

  1. build-system: Określa backend budowania (setuptools, hatchling, poetry-core, flit-core)
  2. project: Podstawowe metadane i zależności
  3. project.optional-dependencies: Dodatkowe zależności funkcji (dev, docs, testowanie)
  4. project.scripts: Punkty wejścia do wiersza poleceń
  5. tool.*: Konfiguracja specyficzna dla narzędzi (black, pytest, mypy, itp.)

Wybór backendu budowania

Setuptools (standardowy wybór)

Najbardziej powszechnie używany i kompatybilny wybór:

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

Zalety: Uniwersalna kompatybilność, szeroki zestaw funkcji, duża ekosystem Wady: Większa konfiguracja, wolniejszy niż nowoczesne alternatywy

Hatchling (nowoczesny i szybki)

Lekki i wydajny nowoczesny backend:

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

Zalety: Szybkie budowanie, prosta konfiguracja, dobre domyślne ustawienia Wady: Mniej wtyczek niż setuptools

Poetry (wszechstronny)

Kompletna zarządzanie zależnościami i środowiskami:

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

[tool.poetry]
name = "mypackage"
version = "0.1.0"
description = "Świetny pakiet Pythona"
authors = ["Twoje Imię <you@example.com>"]

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

Zalety: Rozwiązywanie zależności, pliki blokujące, zintegrowany workflow Wady: Różna praca, większa powierzchnia narzędzia

Flit (minimalistyczny)

Prosty narzędzie do prostych pakietów:

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

Zalety: Ekstremalnie prosty, minimalna konfiguracja Wady: Ograniczone funkcje dla złożonych pakietów

Budowanie pakietu

Zainstaluj narzędzia budowania

# Zainstaluj nowoczesne narzędzie budowania
pip install build

# Albo dla Poetry
pip install poetry

# Albo dla Hatchling
pip install hatch

Budowanie plików dystrybucyjnych

Używając pakietu build (działa z każdym backendem):

# Wyczyść poprzednie budowania
rm -rf dist/ build/ *.egg-info

# Buduj dystrybucję źródłową i wheel
python -m build

# To tworzy:
# dist/mypackage-0.1.0.tar.gz    (dystrybucja źródłowa)
# dist/mypackage-0.1.0-py3-none-any.whl  (wheel)

Używając Poetry:

poetry build

Używając Hatch:

hatch build

Zrozumienie formatów dystrybucyjnych

Dystrybucja źródłowa (sdist) - .tar.gz

  • Zawiera kod źródłowy i instrukcje budowania
  • Użytkownicy pip budują je podczas instalacji
  • Zawiera testy, dokumentację i inne pliki do rozwoju

Wheel - .whl

  • Przygotowana dystrybucja binarna
  • Szybka instalacja (bez kroku budowania)
  • Platformowa lub czysta Pythona
  • Zalecany format dystrybucyjny

Publikowanie w PyPI

Metoda 1: Ręczne przesyłanie za pomocą Twine (tradycyjna)

# Zainstaluj twine
pip install twine

# Sprawdź pakiet przed przesyłaniem
twine check dist/*

# Prześlij do TestPyPI (zalecane)
twine upload --repository testpypi dist/*

# Testuj instalację z TestPyPI
pip install --index-url https://test.pypi.org/simple/ mypackage

# Prześlij do produkcji PyPI
twine upload dist/*

Skonfiguruj poświadczenia w ~/.pypirc:

[distutils]
index-servers =
    pypi
    testpypi

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

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

Metoda 2: Publikacja zaufana (zalecana do CI/CD)

Publikacja zaufana używa OpenID Connect (OIDC) do uwierzytelniania z platform CI/CD bez przechowywania tokenów.

Konfiguracja w PyPI:

  1. Przejdź do ustawień projektu w PyPI
  2. Przejdź do sekcji “Publikacja”
  3. Dodaj nowego “pending publisher”
  4. Skonfiguruj:
    • Właściciel: Twoje imię użytkownika/organizacji na GitHub
    • Repozytorium: Nazwa repozytorium
    • Workflow: publish.yml
    • Środowisko: release (opcjonalnie, ale zalecane)

Workflow GitHub Actions (.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

Zalety:

  • Brak tokenów API do zarządzania lub zabezpieczania
  • Automatyczne uwierzytelnianie za pomocą OIDC
  • Wzmacniana bezpieczeństwo poprzez reguły ochrony środowiska
  • Śledzenie wszystkich publikacji

Najlepsze praktyki

1. Zarządzanie wersjami

Użyj semantycznego numerowania wersji (SemVer): MAJOR.MINOR.PATCH

# Zainstaluj narzędzie do zwiększania wersji
pip install bump2version

# Zwiększ wersję
bump2version patch  # 0.1.0 -> 0.1.1
bump2version minor  # 0.1.1 -> 0.2.0
bump2version major  # 0.2.0 -> 1.0.0

Skonfiguruj w .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. Uwzględnij istotne pliki

README.md: Jasne opisy projektu, instalacja, przykłady użycia LICENSE: Wybierz odpowiednią licencję (MIT, Apache 2.0, GPL, itp.) CHANGELOG.md: Dokumentuj zmiany między wersjami .gitignore: Wyklucz artefakty budowania, cache, wirtualne środowiska

3. Kompleksowe testy

# Zainstaluj zależności testowe
pip install pytest pytest-cov

# Uruchom testy z pokryciem
pytest --cov=mypackage tests/

# Wygeneruj raport pokrycia
pytest --cov=mypackage --cov-report=html tests/

Skonfiguruj w pyproject.toml:

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

4. Narzędzia jakości kodu

# Formatowanie
black src/ tests/

# Lintowanie
flake8 src/ tests/

# Sprawdzanie typów
mypy src/

# Sortowanie importów
isort src/ tests/

Integruj z 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. Dokumentacja

Użyj Sphinx do dokumentacji:

# Zainstaluj Sphinx
pip install sphinx sphinx-rtd-theme

# Inicjalizuj dokumentację
cd docs
sphinx-quickstart

# Buduj dokumentację
make html

Lub użyj MkDocs dla prostszej dokumentacji opartej na Markdown:

pip install mkdocs mkdocs-material
mkdocs new .
mkdocs serve

6. Integracja ciągła

Testowanie pakietów Pythona na różnych platformach i środowiskach jest kluczowe dla niezawodności. Dla informacji na temat wydajności Pythona w różnych scenariuszach wdrażania, zobacz nasze porównanie Wydajność AWS Lambda: JavaScript vs Python vs Golang, które bada, jak Python działa w środowiskach bezserwerowych.

Kompletny workflow CI/CD (.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 formatting check
        run: black --check src/ tests/
      
      - name: Flake8
        run: flake8 src/ tests/
      
      - name: MyPy
        run: mypy src/

Typowe pułapki i rozwiązania

Problem 1: Błędy importu po instalacji

Problem: Pakiet zainstalowany, ale importy nie działają

Rozwiązanie: Upewnij się, że struktura pakietu jest poprawna z plikami __init__.py i poprawna konfiguracja pyproject.toml:

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

Problem 2: Brakujące zależności

Problem: Pakiet zainstalowany, ale zawodzi w czasie działania z powodu braku zależności

Rozwiązanie: Oznacz wszystkie zależności w czasie działania w pyproject.toml:

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

Problem 3: Konflikty wersji

Problem: Pakiet działa w środowisku deweloperskim, ale zawodzi w środowisku produkcyjnym

Rozwiązanie: Użyj środowisk wirtualnych i określ minimalne wersje:

# Utwórz izolowane środowisko
python -m venv .venv
source .venv/bin/activate  # Na Windows: .venv\Scripts\activate

# Zainstaluj w trybie edycji dla dewelopera
pip install -e ".[dev]"

Problem 4: Duży rozmiar pakietu

Problem: Pakiet zbyt długo pobiera się / instaluje

Rozwiązanie: Wyklucz niepotrzebne pliki za pomocą 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 *

Problem 5: Problemy platformowe

Problem: Pakiet działa na jednym systemie operacyjnym, ale zawodzi na innym

Rozwiązanie: Testuj na wielu platformach za pomocą macierzy budowania CI/CD (patrz przykład powyżej)

Lista sprawdzania przed publikacją

Przed publikacją pakietu w PyPI, upewnij się, że:

  • Wszystkie testy przechodzą na różnych wersjach Pythona
  • Kod jest sformatowany i przeszkolony
  • README.md jest kompletny z przykładami
  • Plik LICENSE jest uwzględniony
  • Numer wersji został zaktualizowany
  • CHANGELOG.md został zaktualizowany
  • Zależności są poprawnie określone
  • Pakiet buduje się bez błędów (python -m build)
  • Pakiet został przetestowany na TestPyPI
  • Dokumentacja jest aktualna
  • Repozytorium Git jest oznaczone wersją
  • Utworzono wydanie GitHub (dla zaufanej publikacji)

Zaawansowane tematy

Punkty wejścia i narzędzia CLI

Tworzenie interfejsów wiersza poleceń:

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

Implementacja w src/mypackage/cli.py:

import click

@click.command()
@click.option('--name', default='Świat', help='Imię do powitania')
def main(name):
    """Przykładowy przykład narzędzia CLI"""
    click.echo(f'Cześć, {name}!')

if __name__ == '__main__':
    main()

Dla rzeczywistego przykładu tworzenia pakietów Pythona, które interagują z zewnętrznymi API, zobacz nasz przewodnik Integracja Ollama z Pythonem, który demonstruje budowanie klientów Pythona z obu integracji REST API i oficjalnej biblioteki.

Systemy wtyczek

Włącz odkrywanie wtyczek:

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

Rozszerzenia C

Dla kodu krytycznego pod względem wydajności:

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

Pakiety przestrzeni nazw

Dla pakietów rozdzielonych w ramach wspólnego przestrzeni nazw:

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

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

Użyteczne narzędzia i zasoby

Narzędzia do rozwoju pakietów

  • cookiecutter: Szablony projektowe dla pakietów Pythona
  • tox: Automatyzacja testów na różnych wersjach Pythona
  • nox: Wersja elastyczna testowania (alternatywa dla tox)
  • pre-commit: Framework wtyczek Git dla jakości kodu
  • commitizen: Standaryzacja komunikatów commit i wersjonowania

Platformy dokumentacji

  • Read the Docs: Darmowe hostowanie dokumentacji
  • GitHub Pages: Hostowanie dokumentacji MkDocs lub Sphinx
  • pdoc: Prosty generator dokumentacji API

Rejestry pakietów

  • PyPI: Oficjalny Python Package Index (pypi.org)
  • TestPyPI: Środowisko testowe (test.pypi.org)
  • Anaconda.org: Dystrybucja pakietów Conda
  • GitHub Packages: Hosting prywatnych pakietów

Monitorowanie i analizy

  • PyPI Stats: Statystyki pobierania pakietów
  • Libraries.io: Monitorowanie zależności i powiadomienia
  • Snyk: Scanning bezpieczeństwa wadliwych zależności
  • Dependabot: Automatyczne aktualizacje zależności

Podsumowanie

Nowoczesne opakowanie Pythona zostało zoptymalizowane pod kątem przyjazności dla deweloperów dzięki standardom takim jak pyproject.toml, potężnym backendom budowania i bezpiecznej publikacji za pośrednictwem zaufanej publikacji. Korzystając z tego przewodnika, możesz tworzyć profesjonalne, utrzyjmalne pakiety Pythona, które skutecznie służą Twoim użytkownikom.

Kluczowe wnioski:

  1. Użyj pyproject.toml dla wszystkich nowych projektów
  2. Wybierz odpowiedni backend budowania dla Twoich potrzeb
  3. Zaimplementuj automatyczne testowanie i CI/CD
  4. Użyj zaufanej publikacji do bezpiecznej, beztokenowej dystrybucji
  5. Obserwuj semantyczne wersjonowanie i utrzymuj logi zmian
  6. Udostępniaj kompleksową dokumentację i przykłady

Ekosystem opakowań Pythona nadal się poprawia dzięki lepszym narzędziom, standardom i funkcjom bezpieczeństwa. Stosuj się do Python Enhancement Proposals (PEPs) i najlepszych praktyk społeczności, aby utrzymać swoje pakiety nowoczesne i utrzyjmalne.

Użyteczne linki

Inne przydatne artykuły na tym serwisie