gabriel / muse public
feat BREAKING transport dev
AI Agent gabriel · 152 days ago · Apr 22, 2026 · Diff

feat(transport): async HTTP/2 push via httpx.AsyncClient

- Replace sync httpx.Client streaming POST with httpx.AsyncClient + asyncio.run() to eliminate bidirectional H2 deadlock - Add _httpx_verify() helper: returns local self-signed cert path for localhost URLs, True (system CA) for everything else - Drop HTTP/1.1 chunked fallback; TransportError raised if httpx absent - Fix mpack.py MuseWireFrameWriter docstring: b'muse'/0x01 (was b'MWP2'/0x02)

Tests (TDD — all green): - test_wire_h2_async_push.py: T25-T28 (16 new tests) T25 AsyncClient used with http2=True T26 bidirectional streaming completes without deadlock T27 MWP frame sequence H→O*→C→E, magic b'muse', version 0x01 T28 WriteError maps to TransportError - test_transport_http2.py: updated T11-T12 mocks to AsyncClient pattern - test_wire_h2_strict.py: updated T16/T22/T24 push mocks to async; fixed __enter__ lambda → MagicMock(return_value=resp) for reliable context manager behaviour; added WriteError to mock_mod

sha256:8ee0f6b7f37cf2ced51deeb495cf2db81d0497953ffdc114a5219267f1be90f5 sha
+57 ~27 −2 symbols
sha256:b39eadfe14ad0abc12dd7b5a92746dbfaf665df239ef0cd81dae86787181293e snapshot
+57
symbols added
~27
symbols modified
−2
symbols removed
0
dead code introduced
Semantic Changes 86 symbols
~ tests/test_wire_h2_async_push.py .py 48 symbols added
+ TestT25AsyncClientUsed class class TestT25AsyncClientUsed L126–172
+ _make_mock_httpx method method _make_mock_httpx L129–136
+ test_async_client_has_http2_true method method test_async_client_has_http2_true L158–166
+ test_async_client_is_instantiated method method test_async_client_is_instantiated L138–142
+ test_result_ok_returned method method test_result_ok_returned L168–172
+ test_sync_client_is_not_used_for_streaming_post method method test_sync_client_is_not_used_for_streaming_post L144–156
+ TestT26BidirectionalNoDeadlock class class TestT26BidirectionalNoDeadlock L179–219
+ _make_mock_httpx_with_progress method method _make_mock_httpx_with_progress L188–197
+ test_async_stream_content_generator_is_consumed method method test_async_stream_content_generator_is_consumed L211–219
+ test_push_completes_with_interleaved_response_frames method method test_push_completes_with_interleaved_response_frames L199–203
+ test_push_with_many_objects_completes method method test_push_with_many_objects_completes L205–209
+ TestT27FrameSequence class class TestT27FrameSequence L226–340
+ _collect_frames method method _collect_frames L229–274
+ test_c_frame_after_all_o_frames method method test_c_frame_after_all_o_frames L287–292
+ test_e_frame_is_last method method test_e_frame_is_last L294–296
+ test_h_frame_is_first method method test_h_frame_is_first L276–278
+ test_mwp_magic_in_every_envelope method method test_mwp_magic_in_every_envelope L304–328
+ test_mwp_version_byte_is_1 method method test_mwp_version_byte_is_1 L330–340
+ test_o_frames_follow_h method method test_o_frames_follow_h L280–285
+ test_object_count_matches method method test_object_count_matches L298–302
+ TestT28WriteErrorMapped class class TestT28WriteErrorMapped L347–417
+ _make_mock_httpx_write_error method method _make_mock_httpx_write_error L350–366
+ test_write_error_message_is_informative method method test_write_error_message_is_informative L391–417
+ test_write_error_raises_transport_error method method test_write_error_raises_transport_error L368–389
+ _aiter function async_function _aiter L64–66
+ _async_client_mock function function _async_client_mock L88–94
+ _async_streaming_response function function _async_streaming_response L69–85
+ _do_push function function _do_push L97–119
+ _make_objects function function _make_objects L57–61
+ _mwp_wrap function function _mwp_wrap L52–54
+ _oid function function _oid L37–38
+ _progress_frame function function _progress_frame L48–49
+ _result_frame function function _result_frame L41–45
+ Any import import Any L22–22
+ AsyncIterator import import AsyncIterator L21–21
+ AsyncMock import import AsyncMock L23–23
+ MPackStreamWriter import import MPackStreamWriter L29–29
+ MagicMock import import MagicMock L23–23
+ MuseWireFrameWriter import import MuseWireFrameWriter L29–29
+ ObjectPayload import import ObjectPayload L30–30
+ PushResult import import PushResult L30–30
+ annotations import import annotations L17–17
+ asyncio import import asyncio L19–19
+ blob_id import import blob_id L28–28
+ msgpack import import msgpack L25–25
+ patch import import patch L23–23
+ pytest import import pytest L26–26
+ struct import import struct L20–20
~ muse/core/mpack.py .py 1 symbol modified
~ muse/core/transport.py .py 2 symbols added, 5 symbols modified
+ _LOCAL_TLS_CERT variable variable _LOCAL_TLS_CERT L209–210
+ _httpx_verify function function _httpx_verify L213–222
~ tests/test_transport_http2.py .py 3 symbols added, 6 symbols modified
+ _async_client_mock function function _async_client_mock L95–100
+ _async_streaming_resp function function _async_streaming_resp L79–92
+ AsyncMock import import AsyncMock L14–14
~ _push
~ call
~ patch
~ tests/test_wire_h2_strict.py .py 4 symbols added, 2 symbols removed, 15 symbols modified
test_httpx_error_does_not_call_open_chunked_connection method method test_httpx_error_does_not_call_open_chunked_connection L134–164
test_stale_wire_client_is_closed_before_push method method test_stale_wire_client_is_closed_before_push L494–532
+ _make_error_mock_mod method method _make_error_mock_mod L127–139
+ test_async_client_created_per_push method method test_async_client_created_per_push L486–511
+ _async_mock_httpx_streaming_response function function _async_mock_httpx_streaming_response L72–85
+ AsyncMock import import AsyncMock L20–20
~ call
~ patch
← Older Oldest on dev
All commits
Newer → Latest on dev

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:8ee0f6b7f37cf2ced51deeb495cf2db81d0497953ffdc114a5219267f1be90f5 --body "your comment"