Chapitre 02.10 — Infrastructure#

Tout ce qui touche aux ressources externes : pool de drivers, builders, screenshotters, compteur d’acts, et les adapters Selenium qui implémentent ces abstractions. Depuis la 1.1.3, un infra/playwright/ parallèle livre les mêmes contrats en saveur Playwright — même pool, même builder, mêmes ports.

Plan#

#FichierSujet
0101-drivers-pool.mdWebDriversPool[Driver] : Semaphore, Queue, warmup avec watchdog, shutdown.
0202-driver-builder.mdDriverBuilder[Driver] : gestion du profile et tmp dirs.
0303-screenshotter.mdScreenshotter[TDriver] + ScreenshotterConfig : burst, healthcheck, threadsafe.
0404-act-counter.mdActCounter + ThreadsBasedActCounter (thread-local).
0505-selenium-adapters.mdcreate_driver (Firefox/Chrome/Edge/Safari), create_drivers_pool, create_screenshotter, driver_healthcheck, SeleniumTitleMixin.
0606-playwright-actor.mdL’acteur PlaywrightDriver : thread propriétaire, marshallisation par submit, call_timeout de liveness, warmup cross-thread, Watcher observe-only.

BuiltWebDriver[Driver]#

type BuiltWebDriver[Driver] = tuple[Driver, Effect]

C’est l’unité d’échange entre le builder et la pool : un tuple (driver, dispose). Le dispose est un Effect (sans argument, sans retour) qui sait nettoyer ce driver précisément (driver.quit() + suppression du profile tmp si nécessaire).

Cette signature simple est la garantie de portabilité : n’importe quelle techno peut produire un BuiltWebDriver[Driver] du moment qu’elle expose un dispose: Effect. Ocarina le prouve en livrant deux backends — Selenium et, depuis la 1.1.3, Playwright — et rien n’empêche d’ajouter Puppeteer ou un fake driver.

WebDriversPool[Driver]#

                     ┌────────────────────────┐
                     │   WebDriversPool       │
                     │   (Semaphore, Queue)   │
                     └──────────┬─────────────┘
                                │ create_driver=lambda: ...
                                ▼
                     ┌────────────────────────┐
                     │   DriverBuilder        │
                     │   (profile, tmpdir)    │
                     └──────────┬─────────────┘
                                │ build_driver=lambda profile_path: ...
                                ▼
                     ┌────────────────────────┐
                     │   create_selenium_     │
                     │   driver (per browser  │
                     │   _build_firefox/etc)  │
                     └────────────────────────┘

atexit.register(pool.shutdown)#

Côté adapter (infra/selenium/create_drivers_pool.py) :

drivers_pool = WebDriversPool(create_driver=..., max_size=max_size, warmup_timeout=warmup_timeout)
atexit.register(drivers_pool.shutdown)
return drivers_pool

L’enregistrement atexit garantit qu’il n’y ait pas de zombie quelle que soit la façon dont le programme s’arrête.