fix: persister le token après chaque opération de données + corriger doc QR code
Cause racine : pronotepy peut rafraîchir (rotater) le token en mémoire pendant l'exécution via refresh() automatique après une PronoteAPIError. L'ancien code ne persistait les credentials qu'après le login initial, pas après les opérations de données. Le token roté en mémoire était perdu → au run suivant, token_login échouait avec le token périmé (KeyError 'dataSec'). Correction : - PronoteClient._persist_credentials() : méthode centralisée qui persiste export_credentials() après chaque opération réussie (get_lessons, get_homeworks, get_messages, get_informations) - Le token rafraîchi par le serveur pendant l'exécution est maintenant toujours persisté, même si le pipeline échoue ensuite Documentation : - .env.example : variables QR plus visibles (exemple qr_token décommentable) - AGENTS.md : QR code depuis le site web Pronote (pas l'app mobile), persistance après chaque opération de données - Wiki GuidePronote : procédure corrigée (site web, pas app Android/iOS), mention de la persistance après chaque opération Tests : 5 nouveaux tests de persistance (691 passés, couverture 94.92%)
This commit is contained in:
@@ -234,6 +234,24 @@ class PronoteClient:
|
||||
self._client = self._connect_password()
|
||||
return self._client
|
||||
|
||||
def _persist_credentials(self) -> None:
|
||||
"""Persiste les credentials d'authentification après une opération réussie.
|
||||
|
||||
Le token pronotepy peut être rafraîchi (rotaté) par le serveur lors d'un
|
||||
appel de données (agenda, devoirs, messages). Cette méthode persiste
|
||||
systématiquement les credentials courantes pour garantir la disponibilité
|
||||
du token valide au prochain run.
|
||||
|
||||
Ne fait rien si aucun :class:`PronoteAuthState` n'est configuré (mode
|
||||
``password``) ou si le client n'est pas connecté.
|
||||
"""
|
||||
if self._auth_state is None or self._client is None:
|
||||
return
|
||||
try:
|
||||
self._auth_state.save(self._client.export_credentials())
|
||||
except Exception as exc:
|
||||
logger.debug("Échec de la persistance des credentials : %s", redact_exception(exc))
|
||||
|
||||
def _connect_password(self) -> pronotepy.Client:
|
||||
"""Connecte le client ``pronotepy`` en mode ``password``.
|
||||
|
||||
@@ -298,7 +316,8 @@ class PronoteClient:
|
||||
try:
|
||||
client = client_class.token_login(**creds)
|
||||
if client.logged_in:
|
||||
self._auth_state.save(client.export_credentials())
|
||||
self._client = client
|
||||
self._persist_credentials()
|
||||
return client
|
||||
# logged_in est False — le token est invalide
|
||||
raise PronoteAuthRotationError(
|
||||
@@ -324,8 +343,8 @@ class PronoteClient:
|
||||
# Enrôlement : premier login via QR code (aucun credential persisté)
|
||||
client = self._enroll_qr_code(client_class)
|
||||
# Persister le token rotaté immédiatement
|
||||
if self._auth_state is not None:
|
||||
self._auth_state.save(client.export_credentials())
|
||||
self._client = client
|
||||
self._persist_credentials()
|
||||
return client
|
||||
|
||||
def _enroll_qr_code(self, client_class: type[pronotepy.Client]) -> pronotepy.Client:
|
||||
@@ -423,6 +442,7 @@ class PronoteClient:
|
||||
read=message.seen,
|
||||
)
|
||||
)
|
||||
self._persist_credentials()
|
||||
return messages
|
||||
except (
|
||||
pronotepy.PronoteAPIError,
|
||||
@@ -461,6 +481,7 @@ class PronoteClient:
|
||||
read=info.read,
|
||||
)
|
||||
)
|
||||
self._persist_credentials()
|
||||
return messages
|
||||
except (
|
||||
pronotepy.PronoteAPIError,
|
||||
@@ -529,6 +550,7 @@ class PronoteClient:
|
||||
content=content.description if content is not None else None,
|
||||
)
|
||||
)
|
||||
self._persist_credentials()
|
||||
return lessons
|
||||
|
||||
def get_homeworks(self, start: date, end: date) -> list[Homework]:
|
||||
@@ -566,4 +588,5 @@ class PronoteClient:
|
||||
html=hw.description,
|
||||
)
|
||||
)
|
||||
self._persist_credentials()
|
||||
return homeworks
|
||||
|
||||
Reference in New Issue
Block a user