fix(client): handle multipart and streaming bodies on paid 402 retry#166
Open
bennytimz wants to merge 1 commit into
Open
fix(client): handle multipart and streaming bodies on paid 402 retry#166bennytimz wants to merge 1 commit into
bennytimz wants to merge 1 commit into
Conversation
Complements PR tempoxyz#156 (bytes/content= body replay) with two additions: 1. Multipart / files= bodies: aread() is called before the first dispatch, buffering the MultipartStream into request._content. The paid retry uses content=request.content so the full multipart payload is replayed. Tests: test_paid_retry_replays_multipart_body, test_paid_retry_replays_put_body, test_paid_retry_replays_patch_body. 2. Async generator bodies: an AsyncByteStream that is not also a SyncByteStream (i.e. AsyncIteratorByteStream from content=async_gen()) cannot be safely buffered for replay — the generator may be infinite, already partially consumed, or tied to a one-shot I/O source. PaymentTransport now raises PaymentError immediately with a clear message directing callers to use a buffered body instead. Test: test_paid_retry_raises_for_streaming_body. Also includes the core fix from tempoxyz#156: await request.aread() before dispatch and content=request.content (not stream=request.stream) on retry, so that all finite body types are correctly replayed.
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR complements #156 (bytes/
content=body replay) and addresses the two body-type gaps that #156 does not cover.What #156 fixed
PR #156 by @Tleaoo fixes the core bug:
PaymentTransportwas reusing the already-consumedrequest.streamon 402 retry, so any POST/PUT/PATCH body was silently dropped. The fix isawait request.aread()before dispatch andcontent=request.content(instead ofstream=request.stream) on retry.What this PR adds on top
1. Multipart /
files=bodies (tested)aread()works forMultipartStreambecause it implements bothSyncByteStreamandAsyncByteStream. The full multipart-encoded bytes are buffered before the first dispatch, and the paid retry sends the identical payload.New tests:
test_paid_retry_replays_multipart_body—files=upload, verifies body and filename are present in the retrytest_paid_retry_replays_put_body— PUT with a bytes bodytest_paid_retry_replays_patch_body— PATCH with a bytes bodyAlso includes
test_paid_retry_replays_request_body(same as #156's test) so this branch is self-contained.2. Async generator bodies — explicit
PaymentErrorcontent=async_gen()in httpx produces anAsyncIteratorByteStreamwhich isAsyncByteStreambut notSyncByteStream. UnlikeMultipartStreamandByteStream, an async generator:PaymentTransport.handle_async_requestnow detects this case before any I/O and raisesPaymentErrorwith a clear message directing callers to use a buffered body instead.New test:
test_paid_retry_raises_for_streaming_body— verifiesPaymentErroris raised immediately with no requests sentDetection logic
ByteStreamandMultipartStreamare both Sync+Async so they pass through. OnlyAsyncIteratorByteStream(async generators) is async-only and is caught.Test plan
tests/test_client.pytests pass (confirmed locally)PaymentErrorbefore any network I/O