Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
Unreleased¶
0.10.1 - 2026-07-30¶
Fixed¶
- Use the installed Cosign CLI for release signing so project Python settings cannot interfere with Sigstore's environment.
Documentation¶
- Added the project logo to the README and documentation header and favicon.
0.10.0 - 2026-07-30¶
Changed¶
- Hardened CI and release automation with immutable Actions, complete quality gates, artifact provenance, protected release tags, and automated changelog assembly.
0.9.0 - 2026-07-22¶
Added¶
- Policy-driven retries for
BaseHttpClient: pass an immutableRetryPolicyto_make_req(retry=...)to configure attempt limits, retryable statuses/exceptions, allowed methods, backoff, andRetry-After. Safe defaults cover idempotent methods only. Streaming and other non-replayable bodies are never sent twice. - Immutable
RetryContext/RetryEventvalues and an optionalretry_observer=callback reportingscheduled,suppressed, andexhausteddecisions. DisconnectResponseandTruncatedResponsesocket-level fault primitives;LatencyResponseis now exported directly fromasyncly.srvmockertoo.- Immutable
RecordedRequestsnapshots in mock-service and proxy history, including method, URL/path, headers, query/path parameters, body, and selected handler.RequestHistoryremains as a deprecated compatibility alias. MissingResponseErrorreplaces the opaqueKeyErrorproduced when a route is selected without a registered response.
Changed (breaking)¶
MockService.register()now raisesUnknownHandlerErrorimmediately for a name not declared by anyMockRoute.- Request history no longer retains a live
aiohttp.BaseRequest; use the immutable fields onRecordedRequest(call.method,call.headers, etc.). - Instrumented retrying clients record every physical attempt independently.
A logical
503 -> 200request therefore produces two request observations.
0.8.0 - 2026-07-14¶
Added¶
- In-flight requests metric:
http_client_in_flightgauge (Prometheus, withmultiprocess_mode="livesum") / up-down counter (OpenTelemetry), driven by new optionalon_request_start/on_request_endhooks onMetricsSink. Existing sinks that don't implement these hooks are unaffected — the client feature-detects them once, when the sink is enabled. operationlabel:_make_reqaccepts an optional keyword-onlyoperation=so endpoint methods can emit a stable logical-operation label (e.g.operation="get_wallet_balance"). Falls back to the resolved route.- Normalized
outcome/error_typeviaasyncly.client.metrics.taxonomy: transport failures map to a fixed vocabulary (timeout,network_error,cancelled, …) instead of raw exception class names. A failure after a response arrived (deserialization/validation) is reported asoutcome="response",error_type="invalid_response"with the real status. BaseMetricsSinkconvenience base providing no-op lifecycle hooks.- Network-phase metrics via
build_trace_config(sink): an aiohttpTraceConfigthe caller attaches to theirClientSession, emittinghttp_client_phase_duration_seconds{operation, phase}fordns,pool_wait,connect,ttfb, andbody_read. Reused connections skip dns/connect (no zero samples). Driven by a new optionalobserve_phasesink hook; the client wires per-request labels through aiohttp'strace_request_ctxautomatically. - Connection-pool metrics:
PrometheusPoolCollector(upstream=...)exposeshttp_client_pool_connections{upstream, state}(active/idle) from a boundTCPConnector. Reads private aiohttp internals defensively (degrades to no output if they change). - Lazy metrics exports from
asyncly.client.metrics(InstrumentableHttpClient,MetricsSink,NoopSink,build_trace_config, and — lazily, behind extras —PrometheusSink,PrometheusPoolCollector,OpenTelemetrySink).
Changed (breaking)¶
- Default Prometheus metric names renamed
asyncly_client_*→http_client_*(now consistent with the OpenTelemetry sink and thehttp_client_*convention). Dashboards/alerts referencing the old names must update. To keep the old names, constructPrometheusSink(namespace="asyncly"). statuslabel removed from the duration histogram (it multiplied the time-series count by the bucket count). The histogram now carries a compactoutcome="response|error"; the fullstatusand 4-wayoutcomeremain on the*_requests_totalcounter.MetricsSink.observe_requestsignature gained keyword-onlyoperationandoutcome. Sinks pinned to the old signature must add these parameters.- Cancelled requests are now correctly classified (
outcome="cancelled"); the previousexcept Exceptionnever caughtasyncio.CancelledError.
0.7.1 - 2026-06-10¶
No functional changes to the library — documentation and packaging only.
Added¶
- Documentation site built with MkDocs Material and published to GitHub
Pages at https://andy-takker.github.io/asyncly/ (versioned via
mike): Overview, Installation, Quickstart, a testing-strategies page, usage guides for every subsystem, and an autogenerated API reference (mkdocstrings). - Google-style docstrings across the public API.
DocumentationURL in the project metadata (shown on PyPI).
Changed¶
- Rewrote
README.rstas a slim, friendlyREADME.mdlanding that links to the full documentation.
0.7.0 - 2026-06-10¶
Added¶
- Proxy support in
BaseHttpClient: new keyword-onlyproxyandproxy_autharguments (also onInstrumentableHttpClient). Set them once on the client or override per request; both are forwarded to aiohttp. - Forwarding mock proxy
start_proxy(*, auth=...)andMockProxyServiceinasyncly.srvmocker. Spins up an in-process HTTP proxy that records every request passing through it and forwards it to the real target, so tests can assert a client genuinely routes through a proxy. SupportsProxy-Authorizationvalidation (returns407and does not forward on mismatch). MirrorsMockService's assertion helpers (assert_called,assert_not_called,get_calls,last_call). HTTP targets only (noCONNECT/HTTPS tunnelling). mock_proxypytest fixture exposing a ready-to-use forwarding proxy.
0.6.2 - 2026-05-11¶
Security¶
- Re-release of 0.6.1 with the
aiohttp>=3.13.3constraint actually included in the published wheel. Version 0.6.1 was tagged before the dependency bump from #29 merged, so the uploaded artifact still allowed vulnerableaiohttp<3.13.3. Both 0.6.0 and 0.6.1 are yanked from PyPI. Installasyncly>=0.6.2to receive the CVE-2025-69223 mitigation.
0.6.1 - 2026-05-11¶
Security¶
- Bump minimum
aiohttpto>=3.13.3to address CVE-2025-69223: a zip-bomb DoS inaiohttp's handling of compressed request/response bodies (affects aiohttp<= 3.13.2). Thanks to @loganaden for the report and fix (#29).
0.6.0 - 2026-05-11¶
Added¶
- Pytest plugin (
asyncly.pytest_plugin) auto-registered viapytest11entry-point. Providesmock_routesandmock_servicefixtures so tests no longer need to wire their ownstart_servicecontext manager. - Request matching via the new
Matchbuilder onMockRoute. Multiple routes can share(method, path)and be dispatched by JSON body, headers (subset), query (subset), or raw body. Routes withoutmatch=act as fallbacks within their group. - Assertion helpers on
MockService:get_calls(name),last_call(name),assert_called(name, *, times=, json=, body=, headers=, query=),assert_not_called(name). RawResponsefor returning arbitrary bytes with arbitrary headers — useful for testing client behavior on malformed JSON or unexpected content types.- TLS support in
start_service(routes, *, ssl_context=...)— pass anssl.SSLContextto serve over HTTPS (MockService.url.scheme == "https"). SequenceResponse(on_exhausted=...)with three modes:"raise"(default, new behavior raisesSequenceExhaustedwith a clear message),"cycle","last". Now exported directly fromasyncly.srvmocker.- New exceptions:
SrvMockerError,SequenceExhausted,UnknownHandlerError.
Changed¶
MockService.register()now emitsDeprecationWarningwhen called with ahandler_namenot declared in anyMockRoute. Will becomeUnknownHandlerErrorin 0.7.SequenceResponseon exhaustion now raises a typedSequenceExhaustedinstead of bubblingRuntimeErrorfrom PEP 479. Default behavior otherwise unchanged.Matchdefensively copiesheadersandquerydict arguments at construction time — caller-side mutation of the original dict no longer affects matcher behavior.
Fixed¶
SequenceResponse([])now raisesValueErroreagerly instead of failing on first use.