### Quickstart: Async GET request with Pydantic model Source: https://github.com/modern-python/httpware/blob/main/README.md Demonstrates making a typed GET request using AsyncClient and decoding the response into a Pydantic model. Requires 'httpware[pydantic]' installation. ```python import asyncio from httpware import AsyncClient from pydantic import BaseModel class User(BaseModel): id: int name: str async def main() -> None: async with AsyncClient(base_url="https://jsonplaceholder.typicode.com") as client: user = await client.get("/users/1", response_model=User) print(user.name) # Leanne Graham asyncio.run(main()) ``` -------------------------------- ### Install and Lint httpware Source: https://github.com/modern-python/httpware/blob/main/docs/dev/contributing.md Commands to clone the repository, install dependencies, and run linting checks. ```bash git clone https://github.com/modern-python/httpware.git cd httpware just install # uv lock --upgrade && uv sync --all-extras --frozen --group lint just lint # eof-fixer + ruff format + ruff check + ty check just test # pytest with coverage ``` -------------------------------- ### Project Installation and Linting Commands Source: https://github.com/modern-python/httpware/blob/main/CLAUDE.md Common commands for installing dependencies, running linters, and performing type checking using 'just' and 'uv'. ```bash just install # uv lock --upgrade && uv sync --all-extras --frozen --group lint just lint # eof-fixer + ruff format + ruff check --fix + ty check just lint-ci # same checks without auto-fixing (used in CI) ``` -------------------------------- ### Install httpware with optional dependencies Source: https://github.com/modern-python/httpware/blob/main/README.md Install the core httpware library or with additional decoders like Pydantic or Msgspec. Use 'all' to include everything. ```bash pip install httpware # core only — no decoder pip install httpware[pydantic] # + PydanticDecoder — BaseModel, dataclasses, primitives, generics pip install httpware[msgspec] # + MsgspecDecoder — Struct, dataclasses, primitives, generics pip install httpware[pydantic,msgspec] # both — BaseModel routes to pydantic, Struct to msgspec pip install httpware[all] # everything (pydantic, msgspec, otel) ``` -------------------------------- ### OpenTelemetry Setup for httpware Source: https://github.com/modern-python/httpware/blob/main/docs/middleware.md Configures OpenTelemetry tracing for httpware, enabling visibility of observability events emitted by components like AsyncRetry. This setup includes an SDK, a span processor, and the HTTPX client instrumentor. ```python from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor, ConsoleSpanExporter from opentelemetry.instrumentation.httpx import HTTPXClientInstrumentor trace.set_tracer_provider(TracerProvider()) trace.get_tracer_provider().add_span_processor(BatchSpanProcessor(ConsoleSpanExporter())) HTTPXClientInstrumentor().instrument() ``` -------------------------------- ### Install httpware Source: https://github.com/modern-python/httpware/blob/main/docs/index.md Install the httpware package using pip. Optional extras for Pydantic and msgspec decoding can be included. ```bash pip install httpware ``` ```bash pip install httpware[pydantic] pip install httpware[msgspec] pip install httpware[pydantic,msgspec] ``` -------------------------------- ### Decoder Dispatch Example with Missing Imports Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-12-delta-audit.md This snippet from docs/index.md demonstrates initializing AsyncClient with PydanticDecoder and MsgspecDecoder. It is missing the necessary import statements, which would lead to a NameError. ```python # pydantic-first (the default when both extras are installed): # - BaseModel -> pydantic # - Struct -> msgspec # - dict, list -> pydantic (first in list) AsyncClient(decoders=[PydanticDecoder(), MsgspecDecoder()]) ``` -------------------------------- ### First Async HTTP Request Source: https://github.com/modern-python/httpware/blob/main/docs/index.md Make a basic asynchronous GET request using httpware's AsyncClient. The response JSON is printed to the console. ```python import asyncio from httpware import AsyncClient async def main() -> None: async with AsyncClient(base_url="https://jsonplaceholder.typicode.com") as client: response = await client.get("/users/1") print(response.json()) asyncio.run(main()) ``` -------------------------------- ### Markdown README Logging Example Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-13-delta-audit.md This snippet from README.md demonstrates logging configuration for httpware.circuit_breaker. It sets the level to WARNING, which suppresses INFO-level recovery events like circuit.half_open and circuit.closed. ```markdown README.md:129 sets httpware.circuit_breaker to WARNING. circuit.half_open and circuit.closed are emitted at INFO (circuit_breaker.py admit/on_success). Users copying the snippet silently miss exactly the events that show the circuit probing and recovering — only opened/rejected (WARNING) appear. ``` -------------------------------- ### Typed Decoding Installation Note Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-12-delta-audit.md This text from README.md describes the installation requirement for typed decoding using response_model. It incorrectly implies that only Pydantic is supported, omitting Msgspec support added in version 0.9.0. ```text Typed decoding via `response_model=` works in both worlds — requires `pip install httpware[pydantic]`. Decode failures (malformed body, schema mismatch) raise `httpware.DecodeError`, a `ClientError` subclass — so `except httpware.ClientError` covers them alongside transport and status errors. ``` -------------------------------- ### Shared AsyncBulkhead Example Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Illustrates sharing an AsyncBulkhead instance across multiple AsyncClient instances to enforce a combined concurrency limit for a downstream service. ```python shared_bulkhead = AsyncBulkhead(max_concurrent=10) async with ( AsyncClient(base_url="https://api.example.com", middleware=[shared_bulkhead, AsyncRetry()]) as a, AsyncClient(base_url="https://api.example.com", middleware=[shared_bulkhead, AsyncRetry()]) as b, ): ... # combined in-flight across a + b is capped at 10 ``` -------------------------------- ### First Sync HTTP Request Source: https://github.com/modern-python/httpware/blob/main/docs/index.md Make a basic synchronous GET request using httpware's Client. The response JSON is printed to the console. ```python from httpware import Client with Client(base_url="https://jsonplaceholder.typicode.com") as client: response = client.get("/users/1") print(response.json()) ``` -------------------------------- ### LoggingMiddleware Example Violating No-Print Invariant Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This Python code example from docs/middleware.md demonstrates the use of `print()` within a `LoggingMiddleware`, which contradicts the project's 'no print' invariant enforced by ruff. It should be updated to use standard logging practices. ```python class LoggingMiddleware: def __call__(self, request: httpx2.Request, next: Next) -> httpx2.Response: # noqa: A002 print(f"-> {request.method} {request.url}") response = next(request) print(f"<- {response.status_code}") return response ``` -------------------------------- ### Commit Message Example Source: https://github.com/modern-python/httpware/blob/main/planning/_templates/plan.md Example of a Git commit message following a conventional commit format, including a subject and a co-authored-by line. ```bash git add path/to/file.py git commit -m ": Co-Authored-By: Claude Opus 4.7 (1M context) " ``` -------------------------------- ### Async Circuit Breaker Example Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Use AsyncCircuitBreaker to prevent repeated calls to a service that is experiencing failures. This example shows how to integrate it with an AsyncClient. ```python from httpware import AsyncClient from httpware.middleware.resilience import AsyncCircuitBreaker breaker = AsyncCircuitBreaker(failure_threshold=3, reset_timeout=60.0) async with AsyncClient( base_url="https://api.example.com", middleware=[breaker], ) as client: response = await client.get("/users/1") ``` -------------------------------- ### Shared Retry Budget Example Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Demonstrates sharing a single RetryBudget instance across multiple AsyncClient instances to manage retries for a common downstream service. ```python import asyncio from httpware import AsyncClient from httpware.middleware.resilience import AsyncRetry, RetryBudget shared = RetryBudget() async def main() -> None: async with ( AsyncClient(base_url="https://api.example.com", middleware=[AsyncRetry(budget=shared)]) as users, AsyncClient(base_url="https://api.example.com", middleware=[AsyncRetry(budget=shared)]) as orders, ): await asyncio.gather(users.get("/users/1"), orders.get("/orders/1")) ``` -------------------------------- ### Minimal AsyncClient Wiring with modern-di Source: https://github.com/modern-python/httpware/blob/main/docs/recipes/modern-di.md This snippet demonstrates the basic setup for injecting an AsyncClient into a modern-di container. It configures the client with a base URL and ensures its asynchronous closer is called upon container teardown. ```python from modern_di import Container, Group, Scope, providers from httpware import AsyncClient class ServiceClients(Group): api = providers.Factory( scope=Scope.APP, creator=AsyncClient, kwargs={"base_url": "https://api.example.com"}, cache_settings=providers.CacheSettings(finalizer=AsyncClient.aclose), ) async def main() -> None: container = Container(scope=Scope.APP, groups=[ServiceClients]) try: client = container.resolve(AsyncClient) response = await client.get("/users/1") print(response.status_code) finally: await container.close_async() # runs the AsyncClient.aclose finalizer ``` -------------------------------- ### Implement a custom sync middleware class Source: https://github.com/modern-python/httpware/blob/main/docs/middleware.md Define a custom middleware class that adheres to the sync middleware protocol. This example logs request and response details. ```python import logging import httpx2 from httpware import Client from httpware import Next _LOGGER = logging.getLogger("myapp.logging_middleware") class LoggingMiddleware: def __call__(self, request: httpx2.Request, next: Next) -> httpx2.Response: # noqa: A002 _LOGGER.info("-> %s %s", request.method, request.url) response = next(request) _LOGGER.info("<- %s", response.status_code) return response with Client(base_url="https://api.example.com", middleware=[LoggingMiddleware()]) as client: client.get("/users/1") ``` -------------------------------- ### AsyncClient Initialization and Method Tests Boilerplate Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This snippet shows the basic structure for testing the AsyncClient, including imports and a helper function for creating clients. It serves as a starting point for more comprehensive lifecycle and construction tests. ```python """Tests for the per-method API surface of AsyncClient.""" from httpware import AsyncClient, NotFoundError def _client_with_handler(handler, **kwargs) -> AsyncClient: ... async def test_get_returns_httpx2_response() -> None: ... ``` -------------------------------- ### Python Circuit Breaker Logging Configuration Suggestion Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-13-delta-audit.md This suggestion addresses the README logging example issue by recommending a change in the logging level or adding a comment about recovery events. ```python use INFO for httpware.circuit_breaker in the snippet, or comment that recovery events fire at INFO. ``` -------------------------------- ### Testing Custom Middleware with MockTransport Source: https://github.com/modern-python/httpware/blob/main/docs/testing.md Demonstrates how to test custom middleware by composing it with a mock transport. This example asserts that the custom middleware correctly adds a header to the outgoing request. ```python async def test_my_middleware_adds_header() -> None: handler = _ResponseSequence([HTTPStatus.OK]) async with AsyncClient( httpx2_client=httpx2.AsyncClient(transport=httpx2.MockTransport(handler)), middleware=[MyHeaderMiddleware()], ) as client: await client.get("https://example.test/x") assert handler.calls[0].headers["X-My-Header"] == "expected-value" ``` -------------------------------- ### Python Imports for httpware Middleware Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-14-deep-audit.md This snippet shows example imports for httpware middleware components, including async and sync versions of request/response handlers and next callables. ```python from httpware.middleware import async_before_request, async_after_response, async_on_error from httpware.middleware import AsyncNext from httpware.middleware import Next ``` -------------------------------- ### Async Timeout and Retry Example Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Combine AsyncTimeout and AsyncRetry to enforce an overall deadline for a sequence of operations, including retries. Place AsyncTimeout outermost to bound the entire operation. ```python from httpware import AsyncClient from httpware.middleware.resilience import AsyncCircuitBreaker, AsyncRetry, AsyncTimeout async with AsyncClient( base_url="https://api.example.com", middleware=[ AsyncTimeout(timeout=10.0), # overall deadline across the whole chain AsyncRetry(max_attempts=3), ], ) as client: response = await client.get("/users/1") ``` -------------------------------- ### Example of Future Tense for Shipped Features Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This text block from planning/engineering.md illustrates the use of future tense for features that have already been released. It highlights a discrepancy between the documentation's tense and the actual release status of changes. ```text The next release renames the async middleware surface to use the `Async*`/`async_*` prefix (aligning with httpx2's convention) and removes the seldom-used `attempt_timeout=` kwarg from `AsyncRetry` — see `planning/specs/2026-06-07-sync-client-design.md` for the rationale. The same release also adds a sync `Client` with full feature parity ``` -------------------------------- ### Handling AsyncClient Type Collision in modern-di Source: https://github.com/modern-python/httpware/blob/main/docs/recipes/modern-di.md This example illustrates a common issue where registering multiple AsyncClient instances with modern-di leads to a DuplicateProviderTypeError. It shows the configuration for two distinct API clients that would cause this collision. ```python class ServiceClients(Group): user_api = providers.Factory( scope=Scope.APP, creator=AsyncClient, kwargs={"base_url": "https://users.example.com"}, cache_settings=providers.CacheSettings(finalizer=AsyncClient.aclose), ) billing_api = providers.Factory( scope=Scope.APP, creator=AsyncClient, kwargs={"base_url": "https://billing.example.com"}, cache_settings=providers.CacheSettings(finalizer=AsyncClient.aclose), ) # At Container(...) construction: # modern_di.exceptions.DuplicateProviderTypeError: Provider is duplicated by type # . To resolve this issue: ... ``` -------------------------------- ### Running Tests with Arguments Source: https://github.com/modern-python/httpware/blob/main/CLAUDE.md Demonstrates how to pass arguments to 'just test' to specify test files or filter tests by name. ```bash just test tests/test_client.py just test tests/test_client.py -k test_get_returns_typed_response ``` -------------------------------- ### Manual Command Execution Source: https://github.com/modern-python/httpware/blob/main/CLAUDE.md Shows how to execute linting and testing commands directly using 'uv' without the 'just' task runner. ```bash uv run ruff format . && uv run ruff check . --fix && uv run ty check uv run pytest ``` -------------------------------- ### AsyncClient Initialization with trust_env=True Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-14-deep-audit.md This snippet demonstrates the default initialization of httpx2.AsyncClient within httpware. It highlights that trust_env is implicitly True, leading to silent honoring of HTTP_PROXY/HTTPS_PROXY environment variables, which could be a security concern in compromised environments. ```python self._httpx2_client = httpx2.AsyncClient(**kwargs) ``` -------------------------------- ### Unreliable OpenTelemetry Installation Check Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This snippet demonstrates an unreliable check for OpenTelemetry installation using `find_spec('opentelemetry')`. This check can incorrectly return true for namespace packages, leading to import errors. ```python is_otel_installed = find_spec("opentelemetry") is not None ``` -------------------------------- ### Basic Sync Client Test with MockTransport Source: https://github.com/modern-python/httpware/blob/main/docs/testing.md Illustrates testing a synchronous httpware client using httpx2.MockTransport. The pattern is similar to async, but uses httpx2.Client and a synchronous handler. ```python from http import HTTPStatus import httpx2 from httpware import Client def test_get_returns_typed_response() -> None: def handler(request: httpx2.Request) -> httpx2.Response: return httpx2.Response(HTTPStatus.OK, request=request, json={"ok": True}) with Client(httpx2_client=httpx2.Client(transport=httpx2.MockTransport(handler))) as client: response = client.get("https://example.test/x") assert response.status_code == HTTPStatus.OK assert response.json() == {"ok": True} ``` -------------------------------- ### Basic Async Client Test with MockTransport Source: https://github.com/modern-python/httpware/blob/main/docs/testing.md Demonstrates testing an async httpware client by providing a httpx2.MockTransport with a synchronous handler. This allows for end-to-end testing of the middleware chain without actual network calls. ```python from http import HTTPStatus import httpx2 from httpware import AsyncClient def handler(request: httpx2.Request) -> httpx2.Response: return httpx2.Response(HTTPStatus.OK, json={"id": 1, "name": "Alice"}) async def test_get_user() -> None: transport = httpx2.MockTransport(handler) async with AsyncClient(httpx2_client=httpx2.AsyncClient(transport=transport)) as client: response = await client.get("https://api.example.test/users/1") assert response.status_code == HTTPStatus.OK assert response.json()["name"] == "Alice" ``` -------------------------------- ### Handling RetryBudgetExhaustedError Source: https://github.com/modern-python/httpware/blob/main/docs/errors.md Example of catching a RetryBudgetExhaustedError and logging relevant details such as the number of attempts and the status code of the last response. ```python except RetryBudgetExhaustedError as exc: _LOGGER.error( "budget exhausted after %d attempts; last_status=%s", exc.attempts, exc.last_response.status_code if exc.last_response is not None else None, ) ``` -------------------------------- ### Sync Circuit Breaker Example Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Use CircuitBreaker for synchronous clients to protect against cascading failures. This snippet demonstrates its usage with a standard Client. ```python from httpware import Client from httpware.middleware.resilience import CircuitBreaker breaker = CircuitBreaker(failure_threshold=3, reset_timeout=60.0) with Client( base_url="https://api.example.com", middleware=[breaker], ) as client: client.get("/users/1") ``` -------------------------------- ### Instantiate Client with trust_env=False Source: https://github.com/modern-python/httpware/blob/main/architecture/client.md To opt out of proxy environment variable support, supply an explicit httpx2 client with trust_env set to False when creating a httpware Client. ```python Client(httpx2_client=httpx2.Client(trust_env=False)) ``` -------------------------------- ### Typed Response Decoding with Pydantic Source: https://github.com/modern-python/httpware/blob/main/docs/index.md Perform an asynchronous request and automatically decode the JSON response into a Pydantic model. This requires the 'pydantic' extra to be installed. ```python from httpware import AsyncClient from pydantic import BaseModel class User(BaseModel): id: int name: str async def main() -> None: async with AsyncClient(base_url="https://api.example.com") as client: user = await client.get("/users/1", response_model=User) print(user.name) ``` -------------------------------- ### Instantiate AsyncClient with trust_env=False Source: https://github.com/modern-python/httpware/blob/main/architecture/client.md To opt out of proxy environment variable support, supply an explicit httpx2 client with trust_env set to False when creating a httpware AsyncClient. ```python AsyncClient(httpx2_client=httpx2.AsyncClient(trust_env=False)) ``` -------------------------------- ### Python Circuit Breaker Test Assertion Example Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-13-delta-audit.md This snippet shows how to assert that a sequence of requests, including a 429, does not cause the circuit breaker to open. ```python add [500, 429, 500, 500] (threshold=2), assert it never opens (handler.calls == 4). ``` -------------------------------- ### Modern-DI 2.x Container Construction (Corrected) Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-13-docs-audit.md This snippet demonstrates the correct way to construct and close the root container in modern-di 2.x, addressing the issues found in the original recipe. It avoids the incorrect 'await' on resolve and uses a try/finally block for asynchronous closing. ```python container = Container() try: # ... use container ... finally: await container.close_async() ``` -------------------------------- ### Outdated URL in planning/engineering.md Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-12-delta-audit.md The documentation in planning/engineering.md contains a stale URL pointing to httpware.readthedocs.io instead of the current GitHub Pages site. This was missed during a previous update. ```text As of 0.7.0, the first-cut user-docs surface is live at (Middleware, Resilience, Errors, Testing guides) and Epic 3 is closed. ``` -------------------------------- ### AsyncRetry Configuration with RetryBudget Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This code demonstrates the instantiation of an AsyncRetry object with specific parameters for max attempts, delay, and a RetryBudget. The budget is configured with a time-to-live and minimum retries per second. ```python AsyncRetry( _sleep=sleeper, max_attempts=max_attempts, base_delay=0.001, max_delay=0.002, budget=RetryBudget(ttl=60.0, min_retries_per_sec=1000.0), ) ``` -------------------------------- ### Use a phase decorator for sync middleware Source: https://github.com/modern-python/httpware/blob/main/docs/middleware.md Apply the `@before_request` decorator to a synchronous function to modify requests before they are sent. This example adds a unique request ID header. ```python import uuid import httpx2 from httpware import Client, before_request @before_request def add_request_id(request: httpx2.Request) -> httpx2.Request: return httpx2.Request( request.method, request.url, headers={**request.headers, "X-Request-ID": uuid.uuid4().hex}, content=request.content, ) with Client(base_url="https://api.example.com", middleware=[add_request_id]) as client: client.get("/users/1") ``` -------------------------------- ### Async vs. Sync Client Description in README/docs Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md The initial description of httpware emphasizes its async nature, potentially misleading users about the availability of sync clients. This snippet highlights the discrepancy. ```text A Python async HTTP client framework for building resilient service clients. `httpware` is a thin opinionated wrapper around `httpx2` — it re-exports `httpx2.Request`/`httpx2.Response` as the public request/response surface, adds a middleware chain (with a built-in resilience suite: `AsyncRetry` + `RetryBudget`, `AsyncBulkhead`), opt-in typed response decoding, and a status-keyed exception tree raised automatically on 4xx/5xx. ``` -------------------------------- ### Module Layout Diagram in CLAUDE.md Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md Illustrates the module layout, specifically noting the AsyncClient wrapper. The original comment omits the sync Client, which is also present in client.py. ```text ├── client.py # AsyncClient (thin wrapper over httpx2.AsyncClient) ``` -------------------------------- ### Python Test for Observability Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-14-deep-audit.md This Python test function checks if event emission works correctly when OpenTelemetry is installed but no active span is present. The absence of an exception is used as the assertion. ```python def test_emit_event_works_when_otel_installed_but_no_active_span() -> None: ... # No assertion needed — the absence of an exception IS the assertion. ``` -------------------------------- ### Import Sync Bulkhead Middleware Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Imports the synchronous Bulkhead middleware class from httpware.resilience. ```python from httpware.middleware.resilience import Bulkhead ``` -------------------------------- ### DecodeError Details Source: https://github.com/modern-python/httpware/blob/main/docs/errors.md Explains the DecodeError, raised when response_model parsing fails. It details the exception's fields: response, model, and original exception, and provides an example of how to catch and log it. ```APIDOC ## `DecodeError` `DecodeError` is raised when `response_model=` is set on a request and the active `ResponseDecoder` failed to parse the response body. The HTTP call itself succeeded — status was 2xx/3xx and the transport delivered the body intact — but the body could not be coerced into the requested model. The exception is raised independently of which decoder is in use (`PydanticDecoder`, `MsgspecDecoder`, or a third-party adapter), so `except httpware.ClientError` is sufficient to cover the response-model decode path. Fields: - `response: httpx2.Response` — the response whose body failed to decode. Status, headers, and the originating `request` are all available via `exc.response.` - `model: type` — the type that was passed as `response_model=`. - `original: BaseException` — the underlying library exception (e.g., `pydantic.ValidationError`, `msgspec.ValidationError`, `msgspec.DecodeError`). Also available via `exc.__cause__`. ```python from httpware import AsyncClient, DecodeError try: user = await client.get("/users/1", response_model=User) except DecodeError as exc: _LOGGER.error( "decode failed for %s into %s: %s", exc.response.request.url, exc.model.__name__, exc.original, ) raise ``` ``` -------------------------------- ### Test Async Client Decoder Bypass Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This test verifies that an explicit decoder bypasses the pydantic fail-fast mechanism for AsyncClient when pydantic is not installed. An equivalent test for the synchronous Client is missing. ```python def test_async_client_accepts_explicit_decoder_without_pydantic() -> None: """An explicit decoder= escapes the fail-fast even when pydantic is 'missing'.""" ... with patch("httpware._internal.import_checker.is_pydantic_installed", False): client = AsyncClient(decoder=_FakeDecoder()) assert client is not None ``` -------------------------------- ### Unguarded Pydantic Import in decoders/pydantic.py Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-14-deep-audit.md This snippet shows the unconditional module-level import of TypeAdapter from pydantic. The subsequent __init__ guard is unreachable if pydantic is not installed, leading to a ModuleNotFoundError instead of the intended ImportError. ```python from pydantic import TypeAdapter from httpware._internal import import_checker ... def __init__(self) -> None: if not import_checker.is_pydantic_installed: raise ImportError(MISSING_DEPENDENCY_MESSAGE) ``` -------------------------------- ### Wrap Lazy OTel Import in Try/Except Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This snippet demonstrates how to wrap a lazy OpenTelemetry import and its usage in a try/except ImportError block to prevent crashes when the import fails, even if the dependency is flagged as installed. ```python if import_checker.is_otel_installed: from opentelemetry import trace # noqa: PLC0415 — lazy by design (optional-extras isolation) trace.get_current_span().add_event(event_name, attributes=attributes) ``` -------------------------------- ### Runtime msgspec References in decoders/msgspec.py Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-14-deep-audit.md This code snippet from decoders/msgspec.py shows runtime references to msgspec.inspect.CustomType and msgspec.inspect.Type. These references can cause a NameError if the msgspec library is not installed, as the module-level import is skipped when is_msgspec_installed is False. ```python if isinstance(info, msgspec.inspect.CustomType): return True ``` ```python if isinstance(value, msgspec.inspect.Type): ``` -------------------------------- ### Import AsyncRetry Source: https://github.com/modern-python/httpware/blob/main/docs/resilience.md Import the AsyncRetry middleware class from httpware. ```python from httpware.middleware.resilience import AsyncRetry ``` -------------------------------- ### MissingDecoderError Information Source: https://github.com/modern-python/httpware/blob/main/docs/errors.md Describes MissingDecoderError, which occurs when response_model is set but no suitable decoder is registered. It details the exception's fields and provides guidance on resolving the issue by installing extras or providing custom decoders. ```APIDOC ## `MissingDecoderError` Raised by `send()` / `send_with_response()` / verb methods when `response_model=` is set but no registered decoder claims the model. Carries: - `model: type` — the `response_model=` value that wasn't claimed. - `registered_names: tuple[str, ...]` — class names of the registered decoders that all rejected the model. Empty tuple means no decoders were registered. The message reads `no decoder for response_model=: `, and the corrective action depends on the hint. The two hints, verbatim: - **No decoders were registered** — install an extra or pass an explicit decoder list: no decoders registered. Install `pip install httpware[pydantic]` or `pip install httpware[msgspec]`, or pass decoders=[...] explicitly. - **Registered decoders all rejected the model** — your `response_model` type is exotic enough that neither built-in claims it; pass a custom `ResponseDecoder` via `decoders=[...]`: registered decoders (PydanticDecoder + MsgspecDecoder) all rejected it. Pass a custom decoder via decoders=[...]. Unlike `DecodeError`, this error fires *before* the HTTP request — no traffic is sent. ``` -------------------------------- ### Import necessary modules for sync middleware Source: https://github.com/modern-python/httpware/blob/main/docs/middleware.md Import the base Middleware class and phase decorators for synchronous middleware. ```python from httpware import Middleware, Next, before_request, after_response, on_error ``` -------------------------------- ### Import ResponseTooLargeError Source: https://github.com/modern-python/httpware/blob/main/planning/releases/0.11.0.md Import the new ResponseTooLargeError exception from the httpware library. ```python from httpware import ResponseTooLargeError ``` -------------------------------- ### Fixing AsyncRetry Import in Testing Example Source: https://github.com/modern-python/httpware/blob/main/planning/audits/2026-06-07-deep-audit.md This Python code snippet demonstrates an asynchronous test that fails due to a missing import for AsyncRetry. The fix involves adding the necessary import statement to the code. ```python async def test_retry_succeeds_after_503() -> None: handler = _ResponseSequence([HTTPStatus.SERVICE_UNAVAILABLE, HTTPStatus.OK]) transport = httpx2.MockTransport(handler) async with AsyncClient( httpx2_client=httpx2.AsyncClient(transport=transport), middleware=[AsyncRetry(base_delay=0.001, max_delay=0.002)], ) as client: ```