Chapter 02.10 — Infrastructure#

Everything that touches external resources: driver pool, builders, screenshotters, act counter, and the Selenium adapters that implement them. Since 1.1.3, a parallel infra/playwright/ ships the same contracts in Playwright flavor — same pool, same builder, same ports.

Plan#

#FileSubject
0101-drivers-pool.mdWebDriversPool[Driver]: Semaphore, Queue, warmup with watchdog, shutdown.
0202-driver-builder.mdDriverBuilder[Driver]: profile and tmp dirs handling.
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.mdThe PlaywrightDriver actor: owner thread, marshalling through submit, the call_timeout liveness ceiling, cross-thread warmup, observe-only Watcher.

BuiltWebDriver[Driver]#

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

The exchange unit between the builder and the pool: a (driver, dispose) tuple. dispose is an Effect (no argument, no return) that knows how to clean up that exact driver (driver.quit() + removal of the tmp profile if needed).

That dead-simple signature is the portability guarantee: any tech can produce a BuiltWebDriver[Driver] as long as it exposes a dispose: Effect. Ocarina proves it ships two such backends — Selenium and, since 1.1.3, Playwright — and nothing stops you adding Puppeteer or a 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)#

On the adapter side (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

The atexit registration ensures no zombies, however the program exits.