Retrait des sections de règles de développement de AGENTS.md, désormais centralisées dans orchestrator.md. Co-Authored-By: Warp <agent@warp.dev>
10 KiB
10 KiB
Guide pour les agents et contributeurs — pronote-sync
Langue de travail : Français (ce fichier et toute la documentation projet).
1. Description du projet
pronote-sync est un pipeline Python qui synchronise les données de Pronote (système de gestion scolaire) vers CalDAV (agendas) et XMPP (notifications).
Spécification complète : Voir
GUIDE_DEV_PYTHON.md.
2. Stack technique
Langage et dépendances
- Python : ≥ 3.13.5 (actuellement 3.14 dans
.venv/) - Dépendances principales :
pydantic>=2.0,pydantic-settings,icalendar,caldav,slixmpp,pronotepy,feedparser,beautifulsoup4,requests,httpx,openai
Outils de développement
- Linter :
ruff(longueur de ligne : 100) - Typage :
mypy(modestrict=true) - Tests :
pytest(couverture ≥ 90%),pytest-mock,responses,aioresponses - Sécurité :
bandit - Pre-commit : hooks configurés pour
ruff,mypy,bandit
Environnement
- Virtualenv :
.venv/(à activer avant toute opération) - Fichier de configuration :
.env(à partir de.env.example)
3. Structure du projet
pronote_sync/
├── config/ # Configuration et paramètres
├── errors.py # Hiérarchie canonique des erreurs du pipeline
├── models/ # Modèles de données (Pydantic v2)
├── sources/ # Connecteurs (Pronote, iCal, etc.)
├── sync/ # Logique de synchronisation
├── synthesis/ # Génération de synthèses (IA)
├── channels/ # Canaux de sortie (CalDAV, XMPP)
├── pipeline/ # Orchestration du pipeline
├── utils/ # Utilitaires (redaction, logging, etc.)
├── cli/ # Interface en ligne de commande
└── __init__.py
tests/
├── fixtures/ # Données de test anonymisées
├── unit/ # Tests unitaires
└── integration/ # Tests d'intégration
Plan de développement : Voir
TODO.mdpour les 15 jalons (M1-M15).
4. Commandes essentielles
Environnement
# Activer le virtualenv
source .venv/bin/activate
# Installer les dépendances de développement
pip install -e ".[dev]"
Qualité de code
# Linter (ruff)
ruff check .
# Formattage (ruff)
ruff format .
# Vérification des types (mypy)
mypy .
# Analyse de sécurité (bandit)
bandit -r pronote_sync/
# Pre-commit (tous les hooks)
pre-commit run --all-files
Tests
# Exécuter les tests
pytest
# Exécuter les tests avec couverture
pytest --cov
CLI
# Exécuter en mode dry-run (simulation)
pronote-sync --dry-run
5. Conventions de code
Typage
- Strict :
mypyen modestrict=true. Tous les paramètres, retours et variables locales doivent être typés. - Pydantic v2 : Tous les modèles de données héritent de
BaseModel. Les modèles de contrat utilisentfrozen=True.
Architecture
- Injection de dépendances : Utiliser
typing.Protocolet une composition root danspipeline/run.py. Interdiction des singletons globaux. - Erreurs : Conserver une seule hiérarchie dans
pronote_sync/errors.py; ne pas créer de doublon danspipeline/steps/errors.py.
Style
- Longueur de ligne : 100 caractères maximum (configuré dans
ruff). - Imports : Triés automatiquement par
ruff(intègreisort).
Tests
- Sans réseau : Utiliser uniquement des mocks (
pytest-mock,responses,aioresponses). - Fixtures : Les données de test doivent être anonymisées et stockées dans
tests/fixtures/.
Comportement
- Idempotence : Deux exécutions identiques sans changement externe doivent produire le même résultat.
- Mode dégradé :
- Si l'IA échoue → retourner un message sans synthèse.
- En mode source
auto, essayer iCal puis utiliserpronotepyuniquement si iCal lève une exception. - En mode source explicite (
icaloupronotepy), ne pas changer silencieusement de source. - En mode
auto, si iCal etpronotepyéchouent → lever une erreur critique explicite. - Une liste vide est un succès valide ; elle ne doit pas être assimilée à une panne.
Contrat des sources Pronote
PRONOTE_URL(connexion API) etPRONOTE_ICAL_URL(flux iCal sensible) sont deux paramètres distincts ; ne pas déduire l'un de l'autre.- Le compte actuellement visé est un compte parent : utiliser
pronotepy.ParentClient(pronote_url, username, password, ent=ent_function). - Résoudre le slug
PRONOTE_ENTvers une fonction depronotepy.entau moyen d'une liste fermée. - Exposer séparément les cours et les devoirs dans le client ; filtrer les devoirs
pronotepysur la date cible. - Les récupérations critiques agenda/devoirs propagent une erreur expurgée ; seuls les messages et informations non critiques peuvent se dégrader en liste vide avec warning.
- Réutiliser un téléchargement/parsing iCal pour l'agenda et les devoirs pendant un même run, sans cache global ni persistant.
Contrat d'authentification QR code / token
- Le mode d'authentification est sélectionné par
PRONOTE_AUTH_MODE:password(défaut) : authentification classique via URL, identifiant, mot de passe et ENT.qr_token: authentification par QR code puis token persistant (pour les instances Pronote utilisant HubEduConnect/EduConnect où l'authentification par mot de passe échoue).
- En mode
qr_token, le premier login utilisepronotepy.qrcode_login(qr_code, pin, uuid)avec les paramètresPRONOTE_QR_CODE_FILE(chemin du JSON QR) etPRONOTE_QR_PIN(PIN SecretStr). - Après chaque login réussi, les credentials exportées par
pronotepy.export_credentials()sont persistées dans.pronote_auth_state.json(permissions0600, format JSON versionné, écriture atomique). Le token rotate à chaque session — le fichier doit être mis à jour après chaque run. - Les logins suivants utilisent
pronotepy.token_login(**credentials)avec le token persisté. - En cas d'échec de
token_login(token expiré/invalide), unePronoteAuthRotationErrorest levée. Cette erreur se propage sans wrapping à traversPronoteFetcheretfetch_stepjusqu'àPipelineRunner.run(), qui :- journalise l'erreur (expurgée) ;
- envoie une notification XMPP actionnable si le canal est disponible et
dry_runest inactif ; - retourne un résultat dégradé
(None, errors).
PronoteAuthRotationErrorest re-levée telle quelle (except PronoteAuthRotationError: raise) dans toutes les couches d'enveloppement du chemin critique (fetch_agenda, fetch_homework, fetch_step). Ne pas l'attraper avecexcept Exceptionsans la re-léver d'abord.- Le fichier
.pronote_auth_state.jsonne doit jamais être committé (couvert par.gitignore). Son contenu (token vivant) ne doit jamais apparaître dans les logs, les messages d'erreur ou les notifications XMPP.
Contrat du provider openai-compatible
- Le provider
openai-compatibleréutiliseOpenAISynthesisProvideravec unbase_urlpersonnalisé ; aucun nouveau provider n'est créé. AI_BASE_URLetAI_MODELsont requis ;AI_API_KEYest requis (MVP).- L'URL doit utiliser
httpssauf siAI_ALLOW_INSECURE_HTTP=true. - Les credentials dans l'URL (
user:pass@host) sont refusés. - Les paramètres sensibles dans la query string sont refusés, y compris ceux sans valeur (
?token). - Les URL malformées ou sans hostname sont rejetées (
ValueErrorcatché). - Aucune manipulation automatique de
/v1n'est effectuée. - Configuration incomplète ou invalide →
Noneavec avertissement (mode dégradé) ; la factory ne lève jamais d'exception. - La factory ne fait aucun appel réseau ; les avertissements utilisent
redact_url().
Documentation (docstrings)
- Obligatoire : Toute fonction, méthode et classe publique doit avoir une docstring.
- Format : Utiliser le format Sphinx/reST (pas Google ou NumPy) pour une compatibilité native avec Sphinx.
- Priorité : Les blocs historiques de
GUIDE_DEV_PYTHON.mdutilisantArgs:/Returns:sont illustratifs ; le format Sphinx/reST défini ici prévaut pour le code de production. - Contenu :
- Une ligne de résumé courte (une phrase).
- Une description étendue optionnelle.
- Les paramètres avec
:param nom:. - Le retour avec
:return:et:rtype:. - Les exceptions avec
:raises TypeException:.
- Modules : Chaque module doit avoir une docstring au niveau module.
- Objectif : Générer une documentation PDF via LaTeX avec Sphinx.
Exemple :
def fetch_ical(url: str) -> str:
"""Récupère le contenu d'un flux iCal Pronote.
:param url: URL du flux iCal (avec token ``icalsecurise``).
:return: Contenu brut du flux iCal.
:rtype: str
:raises requests.RequestException: Si la requête HTTP échoue.
"""
6. Sécurité et secrets
Règles absolues
- Aucun secret en clair : Ni dans le code, ni dans les logs, ni dans les erreurs, ni dans les fixtures.
- Masquage : Utiliser systématiquement
redact_url(),redact_secrets(), etredact_exception()depuisutils/redaction.py. - Chaînage d'exceptions : Ne jamais conserver comme
__cause__ou__context__une exception externe brute susceptible de contenir un secret. Journaliser la version expurgée puis utiliserraise ... from None, ou chaîner une cause elle-même expurgée. - Tests de non-fuite : Vérifier les messages, les logs,
__cause__,__context__et le traceback complet avec des sentinelles distinctes pour chaque secret.
Bonnes pratiques
- Types sécurisés : Les mots de passe et clés API doivent utiliser
pydantic.SecretStr. - Fichier
.env: Jamais committé (couvert par.gitignore). Utiliser.env.examplecomme template.
7. Plan de développement
Le projet suit un plan séquentiel en 15 jalons (M1-M15) décrits dans TODO.md.
- Chaque jalon a des critères d'acceptation à valider avant de passer au suivant.
- Ordre logique : scaffolding → config → modèles → sources → sync → synthèse → canaux → pipeline → CLI → tests → déploiement → docs
8. Référence
| Document | Rôle |
|---|---|
GUIDE_DEV_PYTHON.md |
Spécification complète (architecture, modèles, parsing, déploiement) |
TODO.md |
Plan de développement détaillé (15 jalons) |