Store elapsed time on stream wrapper to avoid reference cycles#948
Conversation
Merging this PR will not alter performance
Comparing Footnotes
|
|
Do we even need the |
Instead of holding a reference back to the Response in BoundSyncStream/ BoundAsyncStream (which creates a reference cycle), store the elapsed timedelta on the stream itself after close. Response.elapsed reads it back from self.stream via duck typing, falling back to a directly-set _elapsed value for cases like mocking. This avoids creating reference cycles that can result in significant extra memory usage. It's an alternative approach to encode/httpx#3733 and I prefer my approach, because it does not use references at all (no weakref). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Interesting observation! It would modify the behavior a bit: elapsed is captured when Response.close() is called, not when the underlying stream closes. Typically these are the same (Response.close calls stream.close), but anyone closing response.stream directly would no longer get an elapsed value. |
Kludex
left a comment
There was a problem hiding this comment.
I don't think the tests were adding anything meaningful. All good here, thanks.
CI disagrees with me. |
Instead of holding a reference back to the Response in BoundSyncStream/ BoundAsyncStream (which creates a reference cycle), store the elapsed timedelta on the stream itself after close. Response.elapsed reads it back from self.stream via duck typing, falling back to a directly-set _elapsed value for cases like mocking.
This avoids creating reference cycles that can result in significant extra memory usage.
It's an alternative approach to encode/httpx#3733 and I prefer my approach, because it does not use references at all (no weakref).