Modern Python Engineering
Chapitre 13
13 - DevOps Python
> **Duree :** 3 semaines > **Objectif :** Maitriser l'ecosysteme DevOps Python : Poetry, CI/CD, Docker, publication.
Cours 13 : DevOps Python
1. Poetry
1.1 Initialisation
poetry new mon-projet
cd mon-projet
poetry add fastapi uvicorn
poetry add --dev pytest ruff mypy
1.2 pyproject.toml
[tool.poetry]
name = "mon-projet"
version = "0.1.0"
description = "Un projet Python moderne"
authors = ["Dev Team"]
readme = "README.md"
[tool.poetry.dependencies]
python = "^3.12"
fastapi = "^0.111.0"
uvicorn = {version = "^0.29.0", extras = ["standard"]}
[tool.poetry.group.dev.dependencies]
pytest = "^8.0"
ruff = "^0.4.0"
mypy = "^1.9.0"
pre-commit = "^3.6.0"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
1.3 Commandes principales
poetry install # Installer les dependances
poetry add requests # Ajouter une依赖
poetry add --dev pytest # Ajouter une dev-dependance
poetry update # Mettre a jour
poetry build # Construire le package
poetry publish # Publier sur PyPI
poetry export -f requirements.txt > requirements.txt # Exporter
2. Ruff
2.1 Linter et formateur
# Linting
ruff check src/
ruff check --fix src/ # Autofix automatique
# Formatting
ruff format src/
ruff format --check src/ # Verifier sans modifier
2.2 Configuration
[tool.ruff]
line-length = 100
target-version = "py312"
select = ["E", "F", "I", "N", "W", "UP", "B", "SIM"]
ignore = ["B905"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
3. Mypy
3.1 Configuration stricte
[tool.mypy]
strict = true
python_version = "3.12"
disallow_untyped_defs = true
warn_return_any = true
warn_unreachable = true
no_implicit_optional = true
check_untyped_defs = true
3.2 Utilisation
mypy src/ --strict
mypy src/ --show-error-codes
mypy src/ --ignore-missing-imports
4. Pre-commit
4.1 Configuration
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.0
hooks:
- id: ruff
- id: ruff-format
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.9.0
hooks:
- id: mypy
args: [--strict]
additional_dependencies: [pydantic]
4.2 Installation
pip install pre-commit
pre-commit install
pre-commit run --all-files
5. Docker
5.1 Multi-stage build
# Stage 1: Builder
FROM python:3.12-slim AS builder
WORKDIR /app
COPY pyproject.toml poetry.lock ./
RUN pip install poetry && \
poetry export -f requirements.txt > requirements.txt
# Stage 2: Runtime
FROM python:3.12-slim
WORKDIR /app
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY src/ src/
COPY alembic/ alembic/
EXPOSE 8000
CMD ["uvicorn", "src.main:app", "--host", "0.0.0.0"]
5.2 docker-compose
version: "3.8"
services:
app:
build: .
ports: ["8000:8000"]
environment:
- DATABASE_URL=postgresql://user:pass@db:5432/app
depends_on: [db]
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: app
POSTGRES_USER: user
POSTGRES_PASSWORD: pass
volumes: ["pgdata:/var/lib/postgresql/data"]
volumes:
pgdata:
6. CI/CD avec GitHub Actions
6.1 Workflow complet
name: CI/CD
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
quality:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install poetry && poetry install
- run: poetry run ruff check src/
- run: poetry run mypy src/
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12", "3.13"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "${{ matrix.python-version }}" }
- run: pip install poetry && poetry install
- run: poetry run pytest tests/ -v --cov=src
publish:
needs: [quality, test]
if: startsWith(github.ref, 'refs/tags/')
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: "3.12" }
- run: pip install poetry
- run: poetry build
- run: poetry publish --username __token__ --password ${{ secrets.PYPI_TOKEN }}
7. Nox
import nox
@nox.session(python=["3.11", "3.12", "3.13"])
def tests(session):
session.install("poetry")
session.run("poetry", "install")
session.run("pytest", "tests/", "-v")
@nox.session
def lint(session):
session.install("ruff")
session.run("ruff", "check", "src/")
@nox.session
def typecheck(session):
session.install("poetry")
session.run("poetry", "install")
session.run("mypy", "src/", "--strict")
nox # Run all sessions
nox -s tests # Run only tests
nox -s lint-3.12 # Run lint on Python 3.12
8. Makefile
.PHONY: install lint format typecheck test build clean
install:
poetry install
lint:
poetry run ruff check src/
format:
poetry run ruff format src/
typecheck:
poetry run mypy src/ --strict
test:
poetry run pytest tests/ -v --cov=src --cov-report=term-missing
build: lint typecheck test
poetry build
clean:
rm -rf dist/ .pytest_cache/ .mypy_cache/ .ruff_cache/
find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true
.PHONY: docker-build
docker-build:
docker build -t mon-app .
.PHONY: docker-run
docker-run:
docker-compose up -d
9. Diagramme CI/CD
Diagramme en cours de génération...
10. Bonnes pratiques
- Toujours utiliser Poetry pour la gestion des dependances
- Ruff pour le linting ET le formatting
- Mypy --strict obligatoire dans la CI
- Pre-commit hooks pour capturer les erreurs localement
- Docker multi-stage pour des images legeres
- Testing matrix multi-version Python
- Publier automatiquement sur PyPI avec les tags git