Remove the dead vcrpy/aiohttp compat shim (closes #2140) - #2164
Merged
Conversation
aiohttp 3.14 removed `aiohttp.streams.AsyncStreamReaderMixin`, which vcrpy 8.1.1's `vcr/stubs/aiohttp_stubs.py` subclassed at import time. vcrpy loads that stub lazily when a cassette is entered, so every VCR cassette entry raised AttributeError (issue #1920). The workaround was `ensure_aiohttp_vcr_compat()`, which re-injected the removed name as an empty class. vcrpy 8.2.0 fixed the stub upstream (kevin1024/vcrpy#996): `MockStream` now inherits only from `asyncio.StreamReader` and handles the 3.14 `stream_writer` argument itself. The pin has been at 8.2.1 or later since then, so the shim has been redundant — and not merely inert. Under aiohttp 3.14 the `hasattr` guard fell through, so conftest import was still writing a fake `AsyncStreamReaderMixin` into the real `aiohttp.streams` namespace on every test run. Verified in two environments, in both cases entering a cassette with no shim applied and confirming `MockStream.__bases__ == (asyncio.StreamReader,)`: - the local django image: vcrpy 8.2.1 + aiohttp 3.14.1 (the exact pre-bump pairing the issue asks about) - a clean venv: vcrpy 8.3.0 + aiohttp 3.14.1 (the current pin) Removed: - `ensure_aiohttp_vcr_compat()` in `opencontractserver/utils/vcr_replay.py` and its call in `maybe_vcr_cassette()` - the import and module-level call in `conftest.py` - `EnsureAiohttpVcrCompatTests` in `opencontractserver/tests/test_vcr_replay.py` - the `requirements/local.txt` comment, which still described vcrpy 8.1.1 as the pinned version and #996 as unreleased; replaced with a short note on why the floor is 8.2.0 The `aiohttp>=3.13,<3.14` cap this was paired with (issue #1914) was already lifted on main. `EnsureAiohttpVcrCompatTests` is replaced by `VcrCassetteEntryTests`, which keeps the part of the guard that survives the shim: entering a cassette forces the lazy import of `vcr/stubs/aiohttp_stubs.py`, so the test fails if a future vcrpy/aiohttp pairing reintroduces an import-time incompatibility. 72 tests pass across `test_vcr_replay.py`, `test_structured_response_simple.py`, `test_structured_response_api.py`, and `test_individual_extract_tasks.py`.
ReviewClean, well-substantiated deletion PR. The investigation in the description (verifying
Code quality
Minor nit (non-blocking)
Process note
No functional issues found. This is a safe, well-tested removal. |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
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.
Closes #2140.
Finding: the shim is dead, and confirmed so
aiohttp 3.14 removed
aiohttp.streams.AsyncStreamReaderMixin, which vcrpy 8.1.1'svcr/stubs/aiohttp_stubs.pysubclassed at import time. vcrpy loads that stub lazily when a cassette is entered, so every VCR cassette entry raisedAttributeError(issue #1920). The workaround wasensure_aiohttp_vcr_compat(), which re-injected the removed name as an empty class.vcrpy 8.2.0 fixed the stub upstream (kevin1024/vcrpy#996). In 8.2.1 and 8.3.0,
MockStreaminherits only fromasyncio.StreamReaderand handles the aiohttp 3.14stream_writerargument itself. The pin has been at 8.2.1 or later since then, so the issue's hypothesis is correct: the shim went dead before the 8.2.1 → 8.3.0 dependabot bump in #2132, not because of it.One correction to the issue's framing: the shim was not merely inert. Under aiohttp 3.14 its
hasattrguard fell through, so conftest import was still writing a fakeAsyncStreamReaderMixininto the realaiohttp.streamsnamespace on every test run. Removing it also removes that namespace pollution.Verification
Entered a cassette with no shim applied and checked
MockStream.__bases__in two environments:__bases__ == (asyncio.StreamReader,)__bases__ == (asyncio.StreamReader,)The first row is the exact pre-bump pairing the issue asks about, which is what makes it decisive: the shim was already redundant at the old pin.
The three non-pytest
maybe_vcr_cassette()call sites —opencontractserver/tasks/data_extract_tasks.py:637(celery worker) andconfig/websocket/consumers/unified_agent_conversation.py:1163/:1431(Daphne) — are exercised byfrontend-e2e-extract.ymlandfrontend-e2e-websocket.ymlinOC_LLM_VCR_MODE=replay. Both build vialocal.yml, andcompose/local/django/Dockerfile:59installsrequirements/local.txt, so they are protected by the version pin rather than by the deleted call.Changes
ensure_aiohttp_vcr_compat()fromopencontractserver/utils/vcr_replay.pyand its call inmaybe_vcr_cassette()conftest.pyEnsureAiohttpVcrCompatTestswithVcrCassetteEntryTestsinopencontractserver/tests/test_vcr_replay.pyrequirements/local.txtcomment, which still described vcrpy 8.1.1 as the pinned version and Add Redis integration tests and CI workflow #996 as unreleasedEnsureAiohttpVcrCompatTeststested the shim itself, so it goes with the shim.VcrCassetteEntryTestskeeps the part of the guard that survives: entering a cassette forces the lazy import ofvcr/stubs/aiohttp_stubs.py, so it fails if a future vcrpy/aiohttp pairing reintroduces an import-time incompatibility. The review agent confirmed this by reproducing vcrpy 8.1.1's class line against aiohttp 3.14.1 — both new tests fail with the originalAttributeError.The
aiohttp>=3.13,<3.14cap this was paired with (issue #1914) was already lifted on main; no requirements file pins aiohttp today.Testing
72 passing locally across the targeted VCR-consuming modules:
test_vcr_replay.py,test_structured_response_simple.py,test_structured_response_api.py,test_individual_extract_tasks.py. The remaining VCR modules are slow integration suites and are left to CI — the mechanism is proven at conftest-import level, so they would re-confirm the same thing. pre-commit clean (black, isort, flake8, mypy, changelog fragment validation).Note for the release editor
changelog.d/1920-aiohttp-cap.changed.md:15citesopencontractserver/tests/test_vcr_replay.py::EnsureAiohttpVcrCompatTestsas its regression test — the class this PR renames. That fragment is another PR's record and the one-file-per-PR convention says not to edit it here, so it is left alone; worth repointing atVcrCassetteEntryTestsduring collation. The three aiohttp/vcrpy fragments (#1914 cap added, #1920 cap lifted + shim added, #2140 shim removed) all land in the same[Unreleased]batch and net to zero, so they may be worth folding into one line at that point.