Parent
#27
What to build
Migrate the HTTP layer from requests to httpx and make the entire ClientProtocol async. This is the foundational slice — everything else depends on it.
Client protocol: Every method in ClientProtocol becomes async def (coroutine). The protocol remains a single interface — no sync/async split.
Real client: Client switches from requests to httpx.AsyncClient. The AsyncClient is created in Client.__init__() (httpx does not require an active event loop at construction time). Add a close() method to both the protocol and the implementation that closes the underlying httpx.AsyncClient.
Test client: TestClient methods become async def to satisfy the updated protocol. Replace collections.deque for _incoming_calls with asyncio.Queue. poll_ki_call() uses await self._incoming_calls.get() so it blocks (async) until a request is enqueued — simulating the SC long-polling behavior. Test helper methods (enqueue_handle_request, mock_result_binding_set, enqueue_exit) stay synchronous, using queue.put_nowait().
Dependencies: Replace requests with httpx in pyproject.toml. Add pytest-asyncio as a dev dependency.
Tests: Migrate ALL existing tests to async def + @pytest.mark.asyncio so the test suite passes against the new async protocol. Existing test patterns (TestClient injection, enqueue/mock/assert) should be preserved — only the async wrapping changes.
Acceptance criteria
Blocked by
None — can start immediately
Parent
#27
What to build
Migrate the HTTP layer from
requeststohttpxand make the entireClientProtocolasync. This is the foundational slice — everything else depends on it.Client protocol: Every method in
ClientProtocolbecomesasync def(coroutine). The protocol remains a single interface — no sync/async split.Real client:
Clientswitches fromrequeststohttpx.AsyncClient. TheAsyncClientis created inClient.__init__()(httpx does not require an active event loop at construction time). Add aclose()method to both the protocol and the implementation that closes the underlyinghttpx.AsyncClient.Test client:
TestClientmethods becomeasync defto satisfy the updated protocol. Replacecollections.dequefor_incoming_callswithasyncio.Queue.poll_ki_call()usesawait self._incoming_calls.get()so it blocks (async) until a request is enqueued — simulating the SC long-polling behavior. Test helper methods (enqueue_handle_request,mock_result_binding_set,enqueue_exit) stay synchronous, usingqueue.put_nowait().Dependencies: Replace
requestswithhttpxinpyproject.toml. Addpytest-asyncioas a dev dependency.Tests: Migrate ALL existing tests to
async def+@pytest.mark.asyncioso the test suite passes against the new async protocol. Existing test patterns (TestClient injection, enqueue/mock/assert) should be preserved — only the async wrapping changes.Acceptance criteria
requestsremoved frompyproject.tomldependencies;httpxaddedpytest-asyncioadded as dev dependencyClientProtocolmethods areasync defClientuseshttpx.AsyncClientinternally for all HTTP callsClienthas aclose()method that closes thehttpx.AsyncClientTestClientimplements the asyncClientProtocolTestClient.poll_ki_call()usesasyncio.Queueand blocks until items are availableTestClienttest helpers (enqueue_handle_request,mock_result_binding_set,enqueue_exit) remain synchronousuv run ruff check .passesBlocked by
None — can start immediately