bump the aioweb dependency from the stale v0.1.0 to current v0.1.5 — seam-verified against
v0.1.5's actual API (request_with_retries -> Response on success, falsy FailureResponse on
failure, all four override seams present). backend raw_request catches narrowed from bare
Exception to OSError (covers curl_cffi RequestException / noble TLSClientException) and
re-raises ClientError/TimeoutError first, so a real bug isn't laundered into 'client error'.
Signed-off-by: disqualifier <dev@disqualifier.me>
- CurlCffi/Noble raw_request translate backend-native network errors (curl_cffi
RequestException, noble_tls TLSClientException) into aiohttp.ClientError so the bare
request() path gives the same typed-failure contract as the aiohttp backend (L6)
- Noble.setup uses download_if_necessary (the current noble_tls API), with
update_if_necessary only as a fallback; docstring/CLAUDE.md no longer claim the dead
'refreshes an existing one' path (L7)
- pyproject description says composition (one injectable TLSSession), not the old
'ExtendedSession subclasses' (L8).
Signed-off-by: disqualifier <dev@disqualifier.me>
per-call impersonate= is honored only via the low-level request()/_raw_request path
(which forwards **kwargs to the backend), NOT request_with_retries — its inherited
aioweb signature is fixed with no **kwargs and raises TypeError on an extra kwarg.
docs-only across README + backend docstring + CLAUDE.md; for the retrying path, set
the profile on the CurlCffi/Noble instance. no code change.
Signed-off-by: disqualifier <dev@disqualifier.me>
CurlCffi.is_closed read getattr(session, 'closed', False), but curl_cffi tracks closed state only in the private _closed and exposes no public 'closed' property, so it always returned False. it now reads _closed, falling back to a public 'closed' if a future version adds one. TLSSession's own flag remains the primary signal; this is the best-effort backend check for out-of-band closes.
Signed-off-by: disqualifier <dev@disqualifier.me>
noble_tls.Session takes neither headers nor timeout in its constructor, and Noble.create_session forwarded only client+kwargs, so TLSSession(backend=Noble(...), headers=...) silently dropped the headers while CurlCffi passed them through. create_session now applies headers via session.headers.update and sets timeout_seconds after construction. verified against the contract with a stubbed noble_tls; the real Go-lib + a live request remain an untested gap (noble_tls not installable in this env).
Signed-off-by: disqualifier <dev@disqualifier.me>
Noble.setup guarded the one-time Go shared-library fetch with a bare 'if self._updated' flag — a TOCTOU race where concurrent first requests both passed the check before either set the flag, running the download multiple times. now guarded by a per-instance asyncio.Lock with a check-lock-recheck. verified under load: 2/10/100/500 concurrent setups run the fetch exactly once each (a no-lock control runs it N times).
Signed-off-by: disqualifier <dev@disqualifier.me>
int(timeout) truncated a fractional timeout (e.g. 0.5s) to 0, which noble treats as
no/instant timeout. round up with math.ceil and floor at 1 so a sub-second timeout
stays a real (>=1s) timeout.
verified: 0.5/0.1/0.001 -> 1 (was 0); whole seconds unchanged.
Signed-off-by: disqualifier <dev@disqualifier.me>
TLSSession over aioweb's backend seam by composition: one session class
delegates the four seams to an injected backend. ships CurlCffi (curl_cffi
impersonate) and Noble (noble_tls Client) backends plus the TLSBackend
protocol for custom clients. tls clients are optional extras
([curl]/[noble]/[all]) with guarded imports; all aioweb features (domain/
header/ephemeral/proxy/retry/preview) inherited unchanged. src/ multi-module
layout, hatchling.
Signed-off-by: disqualifier <dev@disqualifier.me>