fix: dedupe pass-through route.call_count across transport retries (#126)#320
Open
phillza wants to merge 2 commits into
Open
fix: dedupe pass-through route.call_count across transport retries (#126)#320phillza wants to merge 2 commits into
phillza wants to merge 2 commits into
Conversation
When a pass-through route is hit, the underlying transport (typically
httpcore) may retry a failed network operation by re-invoking the same
request through respx. Each invocation flows through Router.resolver,
matches the pass-through route, and records a call - so a single
user-initiated httpx call that fails and retries internally shows
up as call_count == 2.
The original report:
with respx.mock as mock:
route = respx.get('https://example.com').pass_through()
httpx.get('https://example.com') # fails with ConnectError
route.call_count # was 2, expected 1
httpcore rebuilds httpx.Request on retry, so id(request) is not stable
across the two invocations. This fix deduplicates by (method, url, body)
in Router._seen_pass_through_request_keys, populated by the resolver's
PassThrough branch and cleared by Router.reset().
Two user-initiated calls with the same method/url/body would also be
deduplicated - the trade-off is documented in the helper docstring;
the over-count from a single failed retry is the much more common
surprise than two rapid-fire identical GETs.
Closes lundberg#126
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.
Fix pass-through
route.call_countovercounting (#126)Summary
When a
pass_through()route is hit, the underlying transport (typicallyhttpcore) may retry a failed network operation by re-invoking the same request through respx. Each invocation flows throughRouter.resolver, matches the pass-through route, and records a call — so a single user-initiatedhttpxcall that fails and retries internally shows up ascall_count == 2.Root cause
httpcorerebuilds thehttpx.Requeston retry, soid(request)is not stable across the two invocations. The originalRouter.recordtherefore sees two distinct request objects entering theexcept PassThroughbranch and records both.Fix
Routernow keeps aSet[Tuple[str, str, bytes]]of(method, url, body)keys for requests already recorded as pass-through. The dedup key is stable across retries because it is content-based, not identity-based.Trade-off: two user-initiated calls with identical method, URL, and body would also be deduplicated. This is documented in
_pass_through_request_key's docstring. The over-count from a single failed retry is the much more common surprise than two rapid-fire identicalGETs.The set is cleared by
Router.reset(), so a freshrespx.mocksession starts counting from zero again.Tests
Three new tests in
tests/test_router.py:test_pass_through_dedupes_internal_retries— direct reproducer for Overcounting pass-through requests #126test_pass_through_distinguishes_different_bodies— POSTs with different bodies are not deduplicatedtest_pass_through_dedup_clears_on_reset—reset()clears the dedup setFull suite: 324 passed, 2 skipped (intentional), 100% coverage maintained.
Files changed
respx/router.py— add_pass_through_request_keyhelper, add_seen_pass_through_request_keysfield toRouter.__init__, clear it inRouter.reset(), use it for dedup in thePassThroughbranch ofRouter.resolvertests/test_router.py— three new testsCHANGELOG.md— entry under[Unreleased]→FixedCloses #126