| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859 |
- """HTTP client pooling utilities."""
-
- from __future__ import annotations
-
- import atexit
- import threading
- from collections.abc import Callable
-
- import httpx
-
- ClientBuilder = Callable[[], httpx.Client]
-
-
- class HttpClientPoolFactory:
- """Thread-safe factory that maintains reusable HTTP client instances."""
-
- def __init__(self) -> None:
- self._clients: dict[str, httpx.Client] = {}
- self._lock = threading.Lock()
-
- def get_or_create(self, key: str, builder: ClientBuilder) -> httpx.Client:
- """Return a pooled client associated with ``key`` creating it on demand."""
- client = self._clients.get(key)
- if client is not None:
- return client
-
- with self._lock:
- client = self._clients.get(key)
- if client is None:
- client = builder()
- self._clients[key] = client
- return client
-
- def close_all(self) -> None:
- """Close all pooled clients and clear the pool."""
- with self._lock:
- for client in self._clients.values():
- client.close()
- self._clients.clear()
-
-
- _factory = HttpClientPoolFactory()
-
-
- def get_pooled_http_client(key: str, builder: ClientBuilder) -> httpx.Client:
- """Return a pooled client for the given ``key`` using ``builder`` when missing."""
- return _factory.get_or_create(key, builder)
-
-
- def close_all_pooled_clients() -> None:
- """Close every client created through the pooling factory."""
- _factory.close_all()
-
-
- def _register_shutdown_hook() -> None:
- atexit.register(close_all_pooled_clients)
-
-
- _register_shutdown_hook()
|