Normes Python
Cette compétence est la source de vérité pour les outils Python et les règles transverses aux projets. Les compétences d'architecture, de style, de test et de révision doivent la référencer plutôt que la répéter.
Pour la prose d'un projet, chargez également technical-writing. Elle invoque humanizer et gère l'emplacement des documents, les horodatages, les métadonnées de révision, l'accessibilité et la qualité rédactionnelle.
Ordre de priorité des règles
Appliquer les règles dans cet ordre :
- Exigences explicites de l'utilisateur ou de l'organisation
- Configuration du dépôt et décisions d'architecture acceptées
- Cette compétence et les autres compétences Python
- PEP 8 et paramètres par défaut des outils
N'affaiblissez pas le socle de sécurité silencieusement. Enregistrez une exception requise, son périmètre, son risque, son propriétaire (si connu) et sa condition de suppression dans le registre des décisions du projet.
Exécution et disposition
- Ciblez Python 3.11 ou plus récent. Respectez un dépôt qui cible une version plus récente.
- Utilisez
from __future__ import annotationslorsque cela améliore la compatibilité ascendante ou résout un ordre d'annotation. Ne l'ajoutez pas mécaniquement aux fichiers qui n'en ont pas besoin. - Préférez une disposition
src/pour les nouveaux paquets distribuables. - Conservez les métadonnées du projet et la configuration des outils pris en charge dans
pyproject.toml. - Utilisez
uvpour les environnements, la résolution des dépendances, le verrouillage et l'exécution des commandes.
Chaîne d'outils prise en charge
| Objectif | Par défaut | Utilisation requise |
|---|---|---|
| Formatage et linting | Ruff | Tous les projets maintenus |
| Vérification de typage | mypy strict | Paquets de production et API publiques |
| Tests et couverture | pytest, pytest-cov, coverage.py | Tous les changements de comportement |
| Linting de sécurité | Règles S de Ruff et Bandit | Paquets de production |
| SAST | Semgrep avec règles du dépôt | CI pour les produits maintenus |
| Audit des dépendances | pip-audit | CI et vérifications de version |
| Journalisation structurée | structlog | Applications et services |
| Tracing | OpenTelemetry | Services réseau et pipelines tracés |
| Gestion des paquets | uv | Tous les projets |
| Hooks Git | pre-commit | Dépôts maintenus |
| Distribution de secrets | Infisical par défaut de l'organisation | Charges de travail utilisant des secrets |
Une décision d'architecture acceptée peut sélectionner un fournisseur équivalent ou omettre un outil opérationnel qui ne convient pas à une petite bibliothèque locale ou à un script. Ruff, le typage, les tests et la gestion sécurisée des dépendances restent la base de référence.
Les outils expérimentaux, y compris les scanners de vulnérabilité basés sur des modèles et les réviseurs IA, sont consultatifs. Ils ne doivent pas constituer des barrières obligatoires tant que l'équipe n'a pas défini une version prise en charge, une commande, une politique d'échec et un propriétaire.
Politique de dépendances et de versions
- Déclarez des plages d'exécution compatibles dans
pyproject.toml. - Validez
uv.lockpour les applications et les environnements de développement reproductibles. Le fichier de verrouillage possède les versions résolues exactes. - Pour les bibliothèques publiées, déclarez des limites inférieures honnêtes, testez les versions Python prises en charge et les limites inférieures importantes, et ne vous fiez pas au fichier de verrouillage comme contrat de consommateur.
- Placez les outils de qualité et de test dans des groupes de dépendances nommés.
- Exécutez les commandes de qualité via l'environnement uv verrouillé. Ne maintenez pas un second ensemble de versions d'outils Python dans pre-commit.
- Mettez à niveau intentionnellement avec
uv lock --upgrade-package <nom>ou une actualisation complète du verrouillage révisée.
Audit canonique des dépendances pour un projet uv :
fichier_exigences="$(mktemp)"
trap 'rm -f "$fichier_exigences"' EXIT
uv export \
--frozen \
--no-dev \
--no-emit-project \
--no-hashes \
--format requirements.txt \
--output-file "$fichier_exigences"
uv run --frozen pip-audit \
--strict \
--no-deps \
--requirement "$fichier_exigences"
L'exportation audite les dépendances d'exécution verrouillées exactes sans demander à pip-audit de résoudre un second graphe. La détection actuelle de projet --locked de pip-audit prend en charge pyproject.toml et pylock.*.toml, pas uv.lock, donc n'utilisez pas pip-audit --locked . comme audit uv.
pip-audit doit être présent dans le groupe de dépendances de qualité. Une dérogation de vulnérabilité doit nommer l'avis, le périmètre concerné, le contrôle compensatoire, le propriétaire (si connu) et la date d'expiration ou de révision.
Commandes de qualité canoniques
Adaptez les chemins des paquets au dépôt, mais conservez une seule source de commandes pour une utilisation locale et CI :
uv sync --frozen
uv run --frozen ruff check .
uv run --frozen ruff format --check .
uv run --frozen mypy <racines-des-paquets>
uv run --frozen bandit -c pyproject.toml -r <racines-des-paquets>
uv run --frozen semgrep scan --config .semgrep.yml <racines-des-paquets>
uv run --frozen pytest
# Exécutez l'audit de dépendances verrouillées canonique ci-dessus.
La CI enregistre la commande et le résultat. Un outil ou une configuration manquant est « indisponible », pas un succès.
Propriété de pre-commit
Épinglez les hooks d'hygiène non Python par version. Invoquez les outils Python comme hooks locaux via uv run --frozen, afin que les hooks locaux et la CI utilisent les versions exactes de uv.lock.
repos:
- repo: local
hooks:
- id: ruff-check
name: ruff check
entry: uv run --frozen ruff check --fix
language: system
types_or: [python, pyi]
- id: ruff-format
name: ruff format
entry: uv run --frozen ruff format
language: system
types_or: [python, pyi]
- id: mypy
name: mypy
entry: uv run --frozen mypy
language: system
pass_filenames: false
args: [<racines-des-paquets>]
Exécutez Bandit, Semgrep, les tests et l'audit de dépendances dans la CI. Ajoutez-les à pre-commit uniquement lorsque leur latence et leurs dépendances conviennent au dépôt.
Décisions minimales de pyproject.toml
Utilisez les noms et racines des paquets du dépôt plutôt que de copier des espaces réservés.
[tool.ruff]
line-length = 88
target-version = "py311"
[tool.ruff.lint]
select = ["E", "F", "I", "N", "W", "B", "S", "UP", "C4", "SIM", "RUF", "D"]
ignore = ["E501"]
[tool.ruff.lint.pydocstyle]
convention = "google"
[tool.mypy]
python_version = "3.11"
strict = true
warn_return_any = true
warn_unused_configs = true
ignore_missing_imports = false
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "-ra --strict-markers --tb=short --import-mode=importlib"
[tool.coverage.run]
branch = true
source_pkgs = ["votre_paquet"]
[tool.coverage.report]
fail_under = 80
show_missing = true
[tool.bandit]
exclude_dirs = ["tests"]
skips = ["B101"]
Documentez chaque ignore avec un périmètre restreint et une raison. Préférez les ignores par fichier aux ignores globaux.
Contrat de journalisation structurée
Les applications émettent ces champs. Les bibliothèques peuvent utiliser la façade de journalisation de l'application hôte et ne doivent pas configurer de gestionnaires globaux à l'importation.
| Champ | Exigence | Signification |
|---|---|---|
timestamp | Toujours | ISO 8601 UTC |
level | Toujours | DEBUG, INFO, WARNING, ERROR ou CRITICAL |
event | Toujours | Nom d'événement stable en snake_case |
service | Toujours pour les applications | Nom du service ou de l'application |
trace_id | Quand une trace existe | ID de trace OpenTelemetry |
span_id | Quand une span existe | ID de span OpenTelemetry |
error | En cas d'échec | Type d'exception sûr et message |
duration_ms | Pour les opérations mesurées | Millisecondes écoulées |
Les processeurs de niveau de structlog reçoivent généralement des noms de méthode en minuscules. Normalisez après avoir ajouté le niveau :
from typing import Any
import structlog
def mettre_en_majuscule_niveau_log(
_logger: Any,
_method_name: str,
event_dict: dict[str, Any],
) -> dict[str, Any]:
"""Normalise le niveau de journalisation structurée vers le schéma partagé."""
level = event_dict.get("level")
if isinstance(level, str):
event_dict["level"] = level.upper()
return event_dict
processeurs = [
structlog.processors.add_log_level,
mettre_en_majuscule_niveau_log,
structlog.processors.TimeStamper(fmt="iso", utc=True),
]
Testez l'événement final rendu, pas seulement le processeur isolément.
Socle de sécurité
Ne jamais :
- valider ou coder en dur des informations d'identification, jetons, clés privées ou valeurs secrètes réelles
- stocker des secrets réels dans
.envou des fichiers TOML - journaliser des secrets, jetons d'accès, données personnelles sensibles ou corps de requête non restreints
- interpoler des valeurs externes dans du SQL
- utiliser
shell=Trueavec une entrée externe ou variable - désérialiser des données pickle non fiables
- exécuter une entrée non fiable avec
eval,execoucompile - utiliser des arguments par défaut mutables
- utiliser des importations génériques sauf une ré-exportation publique documentée
- ignorer silencieusement une valeur de retour ou une exception sensible à la sécurité
- utiliser un
except:nu
Attrapez Exception uniquement à la limite d'un processus, worker, tâche ou framework où le gestionnaire journalise de manière sécurisée puis relance, mappe l'échec à un code de sortie, ou renvoie une erreur de protocole définie. Attrapez des exceptions spécifiques dans la logique applicative.
Stockez les paramètres ordinaires et les références de secrets dans des fichiers de configuration. Livrez les valeurs secrètes au démarrage via l'injection Infisical ou un équivalent approuvé. Validez la configuration requise avant d'accepter du travail.