Skip to content

Fix aiohttp 3.14 compatibility (AsyncStreamReaderMixin removed, ClientResponse requires stream_writer) - #996

Merged
kevin1024 merged 1 commit into
kevin1024:masterfrom
dsfaccini:fix-aiohttp-314-asyncstreamreadermixin
Jun 9, 2026
Merged

Fix aiohttp 3.14 compatibility (AsyncStreamReaderMixin removed, ClientResponse requires stream_writer)#996
kevin1024 merged 1 commit into
kevin1024:masterfrom
dsfaccini:fix-aiohttp-314-asyncstreamreadermixin

Conversation

@dsfaccini

Copy link
Copy Markdown
Contributor

Symptom

Under aiohttp 3.14.0, importing vcrpy's aiohttp stub crashes:

AttributeError: module 'aiohttp.streams' has no attribute 'AsyncStreamReaderMixin'

and once that's worked around, cassette playback fails with
TypeError: ClientResponse.__init__() missing 1 required keyword-only argument: 'stream_writer'.

Fixes #995.

Root cause

aiohttp 3.14.0 introduced two independent breaking changes to internals that vcrpy's
stub relied on:

  1. aiohttp.streams.AsyncStreamReaderMixin was removed in
    aio-libs/aiohttp#12357. The symbol
    was internal — never in aiohttp.streams.__all__, never documented — so its removal
    isn't treated as a breaking change by aiohttp; the fix belongs here. The mixin only
    provided the async-iteration helpers (iter_chunked, iter_any, iter_chunks) on top
    of StreamReader; in 3.14 those were folded directly into aiohttp.streams.StreamReader.

  2. ClientResponse.__init__ now requires a stream_writer argument and, when
    writer is None, reads stream_writer.output_size.

Fix

  • MockStream no longer subclasses the removed mixin. It keeps asyncio.StreamReader and
    reimplements just the three helpers the mixin added, using aiohttp's own
    AsyncStreamIterator / ChunkTupleAsyncStreamIterator (present in both old and new aiohttp).
    __aiter__/read continue to resolve to asyncio.StreamReader, exactly as before.
  • MockClientResponse passes a zero-sized stream_writer stub only when the running
    aiohttp's ClientResponse accepts that parameter (detected via signature introspection).
    A replayed response has already been "sent", so output_size = 0 is accurate.

Backward compatibility

vcrpy declares no aiohttp version bound (it's a lazily-imported test extra), so this must work
on any aiohttp. No version cap is added. The stream_writer argument is passed only when
present, and the iterator helpers exist in both versions, so the stub imports and plays back
cassettes under aiohttp < 3.14 and >= 3.14 alike.

Verification

  • tests/integration/test_aiohttp.py: 29 passed under both aiohttp 3.14.0 and 3.13.5.
  • Added test_stream_chunked, which exercises response.content.iter_chunked(...) — the
    async-iteration surface that the existing suite never touched and that would otherwise
    silently regress.
  • ruff check + ruff format clean.

🤖 Generated with Claude Code

aiohttp 3.14.0 broke vcrpy's aiohttp stub in two independent ways:

- `aiohttp.streams.AsyncStreamReaderMixin` was removed (internal symbol,
  never in `__all__`; aio-libs/aiohttp#12357), so importing
  `vcr.stubs.aiohttp_stubs` raised `AttributeError` at class-definition time.
  The mixin only added the async-iteration helpers `iter_chunked`/`iter_any`/
  `iter_chunks` on top of `asyncio.StreamReader`; reimplement them directly on
  `MockStream` using aiohttp's `AsyncStreamIterator`/`ChunkTupleAsyncStreamIterator`
  (present in both old and new aiohttp).
- `ClientResponse.__init__` now requires a `stream_writer` argument and, when
  `writer is None`, reads `stream_writer.output_size`. Pass a zero-sized writer
  stub when the parameter is present, detected via signature introspection so
  older aiohttp (which has no such parameter) keeps working.

No aiohttp version cap is added; the stub imports and plays back cassettes under
both aiohttp < 3.14 and >= 3.14. Adds `test_stream_chunked` to guard the
async-iteration surface, which the existing suite did not exercise.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vEpiphyte added a commit to vertexproject/synapse that referenced this pull request Jun 2, 2026
Lower the upper bound on the aiohttp library due to a compatibility issue with vcrpy.

Related issues / PRs
kevin1024/vcrpy#995
kevin1024/vcrpy#996
aio-libs/aiohttp#12357
Mr0grog added a commit to edgi-govdata-archiving/web-monitoring-processing that referenced this pull request Jun 3, 2026
Warcio depends on any version of aiohttp, but vcrpy is not yet compatible with v3.14.x, leading to failing tests. This limits the range of aiohttp versions we allow so that we always have a compatible configuration. See also kevin1024/vcrpy#996 for an upcoming fix in vcrpy.
mdrxy added a commit to langchain-ai/langchain that referenced this pull request Jun 4, 2026
aiohttp **3.14.0** (released 2026-06-01) removed
`aiohttp.streams.AsyncStreamReaderMixin`. The current release of `vcrpy`
(8.1.1) still subclasses it in its aiohttp stub:

```python
class MockStream(asyncio.StreamReader, streams.AsyncStreamReaderMixin):
```

As a result, the VCR fixture fails to import during test setup, and the
scheduled integration tests for the `fireworks` and `xai` partners error
out at `test_stream_time`:

```
AttributeError: module 'aiohttp.streams' has no attribute 'AsyncStreamReaderMixin'
```

Only these two partners are affected because their SDKs use `aiohttp`,
so VCR loads its aiohttp stub; partners on `httpx` (e.g. `anthropic`,
`openai`) are unaffected.

This is an upstream incompatibility tracked at kevin1024/vcrpy#995, with
a fix in kevin1024/vcrpy#996 that has not yet been released.

Until a fixed `vcrpy` ships, this caps `aiohttp<3.14.0` via `[tool.uv]
constraint-dependencies` (the same mechanism already used for the
`pygments` CVE pin) in both packages. Using a constraint rather than
tightening the published `aiohttp>=3.9.1,<4.0.0` range means the change
only affects local/CI resolution — the package metadata users install
against is untouched. Both lockfiles re-resolve to `aiohttp` 3.13.5,
which still provides the removed attribute. A code comment points back
to the upstream fix so the pin can be removed once it lands.
@alibeyram

Copy link
Copy Markdown

any update here?

@michal-samluk

Copy link
Copy Markdown

Hello, is it possible to release this version? It is not avaialbe in pypi. 8.1.2

andrew-blake added a commit to andrew-blake/melcloudhome that referenced this pull request Jun 16, 2026
vcrpy v8.2.0 (released 2026-06-15) fixes the aiohttp 3.14 compatibility
issues that required the <3.15 upper bound (kevin1024/vcrpy#996).

- Remove aiohttp upper bound from pyproject.toml
- aiohttp 3.13.4 → 3.14.1 (fixes several CVEs)
- vcrpy 7.0.0 → 8.2.0
- urllib3 removed (no longer a vcrpy dependency)

Closes #124

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
JSv4 added a commit to Open-Source-Legal/OpenContracts that referenced this pull request Jul 24, 2026
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`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

aiohttp 3.14 incompatibility: AttributeError: module 'aiohttp.streams' has no attribute 'AsyncStreamReaderMixin'

4 participants