Spaces:
Sleeping
fix(ci): désactive le tqdm monitor thread (flake Python 3.12 ubuntu)
Browse filesLe combo ``tqdm._monitor`` + ``ProcessPoolExecutor`` (utilisé par
``picarones.measurements.runner.orchestration`` pour les moteurs
CPU-bound : Tesseract, Pero OCR) provoque un hang du shutdown de
l'interpréteur Python 3.12+ sur ubuntu-latest. Le
``_python_exit`` de ``concurrent.futures.process`` essaie de
joindre les workers du pool, mais le thread monitor non-daemon
de tqdm bloque la sortie globale → hang qui dépasse le timeout
GNU configuré dans ci.yml (9 min) → exit code 124 et job en
failure.
Stack trace observée systématiquement :
File "tqdm/_monitor.py", line 60 in run
File "concurrent/futures/process.py", line 587 in _join_executor_internals
File "concurrent/futures/process.py", line 106 in _python_exit
File "threading.py", line 1594 in _shutdown
Error: Process completed with exit code 124.
Fix : ``tqdm.monitor_interval = 0`` dans ``tests/conftest.py``,
exécuté avant tout import de ``picarones.measurements.runner``.
Le polling thread de tqdm n'a aucune valeur ajoutée en CI (stdout
captured, pas d'affichage interactif) — le désactiver supprime la
contention avec le shutdown de ``ProcessPoolExecutor``.
C'est l'approche standard pour ce flake spécifique sur tqdm +
multiprocessing (cf. issues récurrentes du projet tqdm sur
Python 3.12+).
Vérifications
-------------
- ``pytest tests/`` : 5004 passed, 0 failed (identique au baseline)
- ``ruff check tests/conftest.py`` : clean
- Le ``pytest_sessionfinish`` existant (qui détecte les threads
stuck via ``faulthandler.dump_traceback_later(60)``) continue de
fonctionner — il loggera tout futur thread non-daemon qui fuit.
https://claude.ai/code/session_011XQZNitg1rCgia8ZD1a2hP
- tests/conftest.py +25 -0
|
@@ -49,6 +49,31 @@ os.environ.pop("PICARONES_PUBLIC_MODE", None)
|
|
| 49 |
os.environ.setdefault("PICARONES_RATE_LIMIT_PER_HOUR", "0")
|
| 50 |
|
| 51 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 52 |
def pytest_sessionfinish(session, exitstatus) -> None: # noqa: ARG001
|
| 53 |
"""Diagnostic du shutdown de l'interpréteur.
|
| 54 |
|
|
|
|
| 49 |
os.environ.setdefault("PICARONES_RATE_LIMIT_PER_HOUR", "0")
|
| 50 |
|
| 51 |
|
| 52 |
+
# (3) Désactivation préventive du thread daemon de tqdm.
|
| 53 |
+
# Sur Python 3.12+ (ubuntu-latest en CI), le combo
|
| 54 |
+
# ``tqdm._monitor`` + ``ProcessPoolExecutor`` (utilisé par
|
| 55 |
+
# ``picarones.measurements.runner.orchestration`` pour les moteurs
|
| 56 |
+
# CPU-bound : Tesseract, Pero OCR) provoque un hang du shutdown de
|
| 57 |
+
# l'interpréteur après ``=== passed ===``. Le ``_python_exit`` de
|
| 58 |
+
# ``concurrent.futures.process`` essaie de joindre les workers du
|
| 59 |
+
# pool, mais le thread monitor de tqdm bloque la sortie globale —
|
| 60 |
+
# le hang dépasse le timeout GNU configuré dans ci.yml (9 min) et
|
| 61 |
+
# le job échoue avec exit code 124.
|
| 62 |
+
#
|
| 63 |
+
# ``monitor_interval=0`` désactive le polling thread de tqdm, qui
|
| 64 |
+
# n'est utile qu'à l'affichage interactif des progress bars (sans
|
| 65 |
+
# valeur ajoutée en CI où stdout est captured). Fix idiomatique
|
| 66 |
+
# pour ce flake spécifique.
|
| 67 |
+
try:
|
| 68 |
+
from tqdm import tqdm as _tqdm
|
| 69 |
+
|
| 70 |
+
_tqdm.monitor_interval = 0
|
| 71 |
+
except ImportError: # pragma: no cover
|
| 72 |
+
# tqdm est une dep de prod (cf. pyproject.toml) ; cette branche
|
| 73 |
+
# ne devrait jamais être atteinte en CI mais reste défensive.
|
| 74 |
+
pass
|
| 75 |
+
|
| 76 |
+
|
| 77 |
def pytest_sessionfinish(session, exitstatus) -> None: # noqa: ARG001
|
| 78 |
"""Diagnostic du shutdown de l'interpréteur.
|
| 79 |
|