Skip to content

Request: ability to disable httpx_stubs async patching #944

@jamesbraza

Description

@jamesbraza

I am using httpx-aiohttp to get the interface of httpx with the performance of aiohttp.

Unfortunately the httpx_aiohttp.HttpxAiohttpClient doesn't work out-of-the box with vcrpy because:

  1. httpx-aiohttp has a few details
  2. vcrpy records both request/response pairs:
    1. Once for the inner request/aiohttp.ClientResponse (which can be compressed)
    2. Once for the outer request/httpx.Response pair

For example, with this code:

import httpx_aiohttp
import pytest

@pytest.mark.vcr
@pytest.mark.asyncio
async def test_compressed_get() -> None:
    """Make a GET request, with gzip compression."""
    async with httpx_aiohttp.HttpxAiohttpClient(timeout=60) as client:
        # Can also use https://httpbin.org/gzip, but it can be flaky
        response = await client.get("https://httpbingo.org/gzip")
        response.raise_for_status()
        assert response.status_code == 200
        assert response.json()["gzipped"]

Running this code with Python 3.13, pytest==8.4.1, pytest-asyncio==1.1.0, pytest-recording==0.13.4, vcrpy==7.0.0, httpx==0.28.1, and httpx-aiohttp==0.1.8, httpcore==1.0.9, and no-prerecorded VCR cassette, the .json() call at the end leads to:

json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)

This exposes a failure in httpx-aiohttp (karpetrosyan/httpx-aiohttp#23, also linked above), but it also exposes a duplicated cassette issue in vcrpy (also reported in #938).

vcrpy records 2 VCR cassettes: one for the inner aiohttp request, one for the outer httpx request. These cassettes will be identical with respect to matchers such as ["method", "host", "path", "query"], and note also the headers are identical too. As an example with the above minimal reproducer, they share the same GET method, httpbingo.org host, /gzip path, and [] query.

The request is, can vcrpy expose some way to disable httpx_stubs's async patching, so I can only record one request (the inner aiohttp request)?


The workaround, for those who need it, is in conftest.py just permanently-bypass vcrpy.stubs.httpx_stubs._record_responses:

import vcr.stubs.httpx_stubs

async def _async_vcr_send(cassette, real_send, *args, **kwargs):  # noqa: ARG001
    """VCR send that only sends, not possibly recording or playing back responses."""
    return await real_send(*args, **kwargs)

vcr.stubs.httpx_stubs._async_vcr_send = _async_vcr_send

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions