From 6e13ca42d6ecb8d58fbb8f92ab88ef5cc785c20e Mon Sep 17 00:00:00 2001 From: Pierre-Yves Le Borgne Date: Wed, 30 Sep 2026 23:19:09 +0100 Subject: [PATCH] fix(tests): skip aiohttp stub patches that fixed VCR.py doesn't need _patch_vcr_aiohttp_stubs() works around kevin1024/vcrpy#927 and kevin1024/vcrpy#502. kevin1024/vcrpy#1055 fixes both in VCR.py and makes MockStream aiohttp's StreamReader, which takes the body. The cached content property then calls MockStream() without one, so every aiohttp replay fails with a TypeError and pylint reports E1120. Skip the content property, the no-op set_exception and the record_response replacement when MockStream is a StreamReader, which is only true with that fix. VCR.py 8.3.0 gets the same patches as before. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01GwUvhmzgfkiZ6vo4uGyesN --- py/src/braintrust/conftest.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/py/src/braintrust/conftest.py b/py/src/braintrust/conftest.py index e6cfd460d..7b4559e8e 100644 --- a/py/src/braintrust/conftest.py +++ b/py/src/braintrust/conftest.py @@ -56,6 +56,10 @@ def _patch_vcr_aiohttp_stubs(): is empty. Fix: after reading the body for recording, reset the response's content stream so it can be read again by the caller. + VCR.py releases with kevin1024/vcrpy#1055 fix 1, 3 and 4 themselves, so the + patches for those (including gzip decoding of the content stream) only apply + to older releases. Delete them once uv.lock has such a release. + See: https://github.com/kevin1024/vcrpy/issues/927 """ try: @@ -110,8 +114,6 @@ def patched_response_init(self, method, url, request_info=None): aiohttp_stubs.MockClientResponse.__init__ = patched_response_init - aiohttp_stubs.MockStream.set_exception = lambda self, exc: None - if not hasattr(aiohttp_stubs.MockStream, "iter_chunked"): # Older aiohttp exposed iter_chunked via AsyncStreamReaderMixin. Since # that mixin is gone, VCR.py's MockStream needs the method directly for @@ -134,18 +136,25 @@ async def patched_read(self): return _decompress_body(self._body) aiohttp_stubs.MockClientResponse.read = patched_read + aiohttp_stubs.MockClientResponse._bt_patched = True + + if issubclass(aiohttp_stubs.MockStream, streams.StreamReader): + # VCR.py with kevin1024/vcrpy#1055 (MockStream is then aiohttp's StreamReader) + # fixes 1, 3 and 4 itself, and MockStream() without the body raises there. + return + + aiohttp_stubs.MockStream.set_exception = lambda self, exc: None @property def cached_content(self): if not hasattr(self, "_cached_content"): - stream = aiohttp_stubs.MockStream() + stream = aiohttp_stubs.MockStream() # pylint: disable=no-value-for-parameter stream.feed_data(_decompress_body(self._body)) stream.feed_eof() self._cached_content = stream return self._cached_content aiohttp_stubs.MockClientResponse.content = cached_content - aiohttp_stubs.MockClientResponse._bt_patched = True # Patch record_response to not consume the response body. VCR's original # implementation calls `await response.read()` which exhausts the body,