Skip to content

chore(deps): update dependency vcrpy to v9 - #392

Open
renovate[bot] wants to merge 1 commit into
mainfrom
renovate/vcrpy-9.x
Open

renovate[bot] wants to merge 1 commit into
mainfrom
renovate/vcrpy-9.x

Conversation

@renovate

@renovate renovate Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
vcrpy >=8.3.0 → >=9.4.2 age confidence

Release Notes

kevin1024/vcrpy (vcrpy)

v9.4.2

Compare Source

What's Changed

  • Fix urllib3 pools with block=True, e.g. a requests session with HTTPAdapter(pool_block=True), waiting forever for a free connection in a cassette, or when used before, after or across cassettes. A response now returns its connection to the pool once its body has been read, as in urllib3, and vcrpy keeps a pool's free slots when it takes connections out of it as a cassette starts or stops using the pool. A response finished after its cassette has exited gives back only the slot, not the cassette's connection (#​949, #​1094) - thanks @​nk-batman21

Full Changelog: kevin1024/vcrpy@v9.4.1...v9.4.2

v9.4.1

Compare Source

What's Changed

  • Don't fail with ImportError: cannot import name 'HTTPHeaderDict' on urllib3 1.x, such as the 1.26 that Debian oldstable ships. The urllib3 stub imported HTTPHeaderDict from the top level of urllib3, which only exports it from 2.0 on, so use_cassette() raised while building its patchers and nothing could be recorded (#​1092, #​1093) - thanks @​inchang-ing

Full Changelog: kevin1024/vcrpy@v9.4.0...v9.4.1

v9.4.0

Compare Source

What's Changed

SOCKS proxies

Requests made through SOCKS proxies with requests and urllib3 (socks5:// and socks5h://, with PySocks installed) are now recorded and played back. urllib3 uses its own connection classes for them, which vcrpy didn't patch, so these requests skipped the cassette entirely (#​547, #​548, #​1087).

Multipart form filtering

filter_post_data_parameters now filters the fields of multipart/form-data request bodies, e.g. requests.post(url, data={"api_key": ...}, files=...). Before, it treated them as urlencoded forms and didn't find their fields, so their values were recorded (#​521, #​1090).

If you match on body: requests are filtered before they're matched, so cassettes recorded with unfiltered multipart bodies need re-recording.

Fixes
  • Fix requests escaping a cassette, e.g. a write-protected one not raising CannotOverwriteExistingCassetteException, when a vcr.patch.force_reset() window opened elsewhere is closed late by the garbage collector while the cassette is in use (#​979, #​1051, #​1088) - thanks @​yeion7, from #​1052
  • Fix AttributeError: 'str' object has no attribute 'get_all' when requests builds a response from a urllib.request response, e.g. with HTTPAdapter.build_response() (#​956, #​1089)

Full Changelog: kevin1024/vcrpy@v9.3.2...v9.4.0

v9.3.2

Compare Source

What's Changed

  • Don't open a real connection for HTTPS requests played back from a cassette in the new_episodes record mode. urllib3 connects before sending a request, before vcrpy knows whether it will be played back, so every such request made a TLS handshake, and failed without network access (#​515, #​1086)
  • Raise a clear TypeError when use_cassette() without a path is used as a context manager, instead of KeyError: 'path'. A path can be left out only when decorating a function (#​570)
  • Fix filter_post_data_parameters crashing with AttributeError on iterator request bodies, e.g. a generator passed as data. Like file bodies, they're left unfiltered (#​814)
  • Docs: document inject_cassette, which passes the cassette to a decorated function, e.g. to rewind it or make assertions (#​207, #​493)

Full Changelog: kevin1024/vcrpy@v9.3.1...v9.3.2

v9.3.1

Compare Source

What's Changed

  • Log a warning when a cassette file exists but can't be decoded, instead of silently treating it as empty, where recording could overwrite it (#​501, #​1022) - thanks @​agu2347, and @​philsong4-ai for #​1023
  • Request can be imported from vcr, e.g. vcr.Request("GET", uri, body, headers), as the docs already called it (#​759) - thanks @​lewisdawson

Full Changelog: kevin1024/vcrpy@v9.3.0...v9.3.1

v9.3.0

Compare Source

What's Changed

New features

Cassette metadata (#​934, thanks @​hasier): store values that aren't part of an interaction, such as a seed for random IDs a test sends, so they're the same when a cassette is recorded and played back.

with my_vcr.use_cassette("test.yaml") as cass:
    seed = cass.get_metadata("seed", random.getrandbits(128))

The cassette stores default the first time and returns the stored value after that. If the record mode doesn't allow changing the cassette, a missing key raises MissingMetadataError, a CannotOverwriteExistingCassetteException. See the docs.

skip_patchers (#​944, #​1070, thanks @​smoltafet): leave some HTTP libraries unpatched. For example, skip_patchers=["httpx"] stops an httpx client with an aiohttp transport (httpx-aiohttp) from being recorded twice.

filter_nested_post_data_parameters (#​936, #​937, thanks @​tighwm and @​JargeZ): make filter_post_data_parameters also filter keys in nested objects of JSON request bodies, such as JSON-RPC params. It's opt-in, so existing setups don't filter more than before.

ignore_hosts accepts compiled regular expressions (#​779, thanks @​silviumarcu), which match whole host names: ignore_hosts=["example.com", re.compile(r".*\.actions\.githubusercontent\.com")].

allow_playback_repeats also applies while recording (#​878, #​879, thanks @​pyoor): a request that matches one already recorded gets the recorded response, instead of being sent and recorded again. When played back, such requests got the first response anyway.

Fixes
  • An empty cassette file is recorded like a missing one, instead of crashing with TypeError or JSONDecodeError (#​579, #​1081, thanks @​nk-batman21)
  • aiohttp: requests that aren't recorded, e.g. with ignore_localhost or ignore_hosts, no longer have their response body read by vcrpy, so they stream as usual (#​978, thanks @​AronsonDan)
  • urllib3: release_conn() on a streamed response returns its connection to the pool (#​1084, thanks @​edsonsims)
  • drop_unused_requests no longer saves an interaction once per playback when allow_playback_repeats is set
Docs and CI

Full Changelog: kevin1024/vcrpy@v9.2.0...v9.3.0

v9.2.0

Compare Source

What's Changed

Keep secrets out of cassettes with filter_sensitive_data
my_vcr = vcr.VCR(filter_sensitive_data={"<API_KEY>": os.environ.get("API_KEY", "fake-api-key")})

Every occurrence of the value in a recorded interaction, in the URL, headers and request and response bodies, is replaced with the placeholder when the cassette is saved, and put back when it's loaded. So:

  • the cassette file only contains <API_KEY>,
  • requests made with the real value still match the recorded ones, and
  • your code gets the same responses whether they're recorded or played back, including parts of responses that contained the secret.

Values can also be functions of the interaction. See the docs for using it in CI without the real secret, and for values that are encoded differently in different places. Modeled on Ruby VCR's option of the same name.

Save and load hooks

before_save_interaction and after_load_interaction get each interaction's request and response, plus the cassette if they take a third argument, when a cassette is saved and loaded. They return them, changed or not, or None to leave the interaction out. Unlike before_record_request and before_record_response, they run exactly once per interaction. Some uses, from issues:

  • change a response based on its request, e.g. scrub a token only from a login response (#​527, #​556)
  • keep cookies out of cassettes (#​569)
  • replay part of a cassette and record the rest again (#​892)
  • keep using old cassettes after an API or URL format changes

See the docs for examples. They're modeled on Ruby VCR's before_record and before_playback hooks, and filter_sensitive_data is built on them. Thanks to the authors of #​679 and #​594, which explored this.

Cassettes also have a new read-only path property.

Full Changelog: kevin1024/vcrpy@v9.1.0...v9.2.0

v9.1.0

Compare Source

What's Changed

Fixes
New
  • Replayed urllib3 responses have json(), as urllib3 2.x responses do (#​774). Thanks @​gschnellOnera for the report
  • use_cassette accepts a persister option, to use a custom persister for one cassette (#​575). Thanks @​gioele for the report
Releases
  • vcrpy is now published to PyPI from GitHub Actions with trusted publishing (#​860, #​1078)

Full Changelog: kevin1024/vcrpy@v9.0.0...v9.1.0

v9.0.0

Compare Source

Upgrading from 8.x

9.0.0 has five changes that can affect existing test suites:

  1. Python 3.11+ is required. Python 3.10 reached its end of life on 2026-10-01 (#​1061). On 3.10, pip keeps installing 8.x.
  2. New CannotOverwriteExistingCassetteException message. If your code or tests match on the message text (e.g. "Can't overwrite existing cassette"), update them. The exception class and its cassette and failed_request attributes are unchanged, and a new reason attribute ("cassette_not_found", "already_played", "no_match" or "nothing_similar") is a sturdier thing to check (#​1076).
  3. filter_post_data_parameters now filters request bodies for every HTTP method, not just POST (#​328, #​1004). Requests are filtered before they're matched, so if you match on body, re-record cassettes that have unfiltered PUT/PATCH/DELETE bodies. vcr.filters.replace_post_data_parameters itself still only filters POST; use the new replace_body_parameters for any method.
  4. aiohttp error responses are recorded when raise_for_status is used (on the session or the request), and replaying them raises like aiohttp does (#​635, #​925). Before, they weren't recorded, and a replayed error response didn't raise.
  5. urllib3 (and requests) responses have HTTPHeaderDict headers, as they do without vcrpy, instead of an HTTPMessage (#​841, #​1015).

What's Changed

Much better errors when a request isn't in the cassette

The CannotOverwriteExistingCassetteException message now says what actually went wrong, shows how the closest recorded requests differ, and suggests how to fix it (#​1076). It fixes:

  • "Can't overwrite existing cassette" when the cassette doesn't exist (#​838)
  • no explanation or fix when a request is made more times than it was recorded (#​516, #​673, #​929)
  • empty output for body mismatches, which now show a short diff (#​840)
  • messages tens of MB long for cassettes with many similar requests, which are now summarized (#​837)

For example:

Can't play back this request: it matches 1 recorded interaction, but that has already been played back.
record_mode 'none' doesn't record new requests.

  GET https://example.com/foo

Cassette: tests/cassettes/ex.yaml
  record_mode: none
  match_on: method, scheme, host, port, path, query
  interactions: 1 recorded, 1 played

How to fix:
  * If it's fine for this request to get the same response each time, set
    allow_playback_repeats=True [1].
  * Otherwise record the extra request: run once with record_mode='new_episodes'
    [2].

[1] https://vcrpy.readthedocs.io/en/latest/advanced.html#playback-repeats
[2] https://vcrpy.readthedocs.io/en/latest/usage.html#record-modes

Thanks @​philsong4-ai (#​1027) and @​ChrisJr404 (#​1037) for the PRs that started this.

Fixes
  • aiohttp error responses are recorded and replayed when raise_for_status is used (#​635, #​925). Thanks @​andrescollares
  • filter_post_data_parameters filters request bodies for every HTTP method (#​328, #​1004). Thanks @​andrescollares
  • Headers sent more than once (e.g. two Link headers) keep every value in urllib3 and requests responses (#​947, #​1015). Thanks @​Sanjays2402
  • The body matcher no longer crashes when a body doesn't parse as its headers say, e.g. invalid JSON sent as application/json (#​1075)
Python support

Full Changelog: kevin1024/vcrpy@v8.5.0...v9.0.0

v8.5.0

Compare Source

What's Changed

aiohttp streaming
  • Streaming a response body from response.content (read(), iter_chunked(), readline() loops, ...) while recording no longer returns an empty body (#​502)
  • readline() loops on replayed responses now reach the end of the body instead of restarting from the start on each access. This fixes the infinite loop when replaying google-genai streaming responses (#​927)
  • response.content is now aiohttp's own StreamReader, both when recording and on replay. iter_any() and iter_chunks() work, lines over 64 KiB are no longer refused, and readuntil(), is_eof(), total_bytes and the rest of its API are available

All three are from #​1055 — thanks @​pylaterreur!

If your tests patch vcr.stubs.aiohttp_stubs to work around #​502 or #​927 (for example by overriding MockClientResponse.content, or by putting the recorded body back with unread_data()), remove the patch: it is no longer needed, and no longer works.

Other fixes
  • Fix filter_post_data_parameters crashing with UnicodeDecodeError on binary request bodies passed as bytes or bytearray, which are now left untouched, and stop it mangling form segments without a = (#​660, #​1039). Thanks @​philsong4-ai
  • Fix recording aiohttp requests that upload an open file as a form field (data={"file": f}), which crashed with TypeError: cannot pickle '_io.BufferedReader' object. The file's contents are recorded and the whole file is still sent (#​737, #​1073). Thanks @​LilSpazJoekp for the report
  • Fix recording aiohttp requests whose body is an async generator, e.g. a streaming upload (#​974, #​1069). Thanks @​smoltafet
Python support
  • Support Python 3.14 and 3.15, and test them in CI along with their free-threaded builds, 3.14t and 3.15t (#​940, #​1058). Thanks @​pylaterreur

Full Changelog: kevin1024/vcrpy@v8.4.0...v8.5.0

v8.4.0

Compare Source

What's Changed

Behavior change: the all record mode
  • Fix re-recording in the all record mode (#​1071, fixes #​578 and #​208):
    • A re-recorded interaction now replaces the interaction it matches. Before, the new one was saved next to the old one, and the stale response was replayed first on the next run.
    • With drop_unused_requests=True, the cassette is now replaced with only what was just recorded. Before, it was saved empty.
    • Behavior change: re-recording now removes every previously recorded interaction that matches a new one under your match_on setting. With loose matchers (e.g. match_on=["method"]), one new request can replace many old ones. Cassettes re-recorded in all mode also no longer accumulate responses across runs.
Fixes
  • Fix httpx and httpx2 replay crashing on cassettes that store a null reason phrase, which includes cassettes recorded before 8.2.0 (#​1028, #​1029). Thanks @​dgunning
  • Fix bare-host URLs such as https://example.com not matching cassettes that recorded them as https://example.com/, a regression in 8.2.0 (#​1013, #​1031). The uri matcher is otherwise as strict as before. Thanks @​DSeaStar
  • Fix AttributeError: 'VCRFakeSocket' object has no attribute 'gettimeout' when niquests reuses a pooled connection with urllib3-future 2.25.901 or later (#​1056). Thanks @​pylaterreur
  • Streaming (file-like or iterator) request bodies are now forwarded with Content-Length instead of chunked encoding, which some servers reject (#​1002)
Maintenance

Full Changelog: kevin1024/vcrpy@v8.3.0...v8.4.0


Configuration

📅 Schedule: (UTC)

  • Branch creation
    • Only on Sunday and Saturday (* * * * 0,6)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate renovate Bot added breaking dependencies Pull requests that update a dependency file labels Oct 31, 2026
@renovate

renovate Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor Author

⚠️ Artifact update problem

Renovate failed to update an artifact related to this branch. You probably do not want to merge this PR as-is.

♻ Renovate will retry this branch, including artifacts, only when one of the following happens:

  • any of the package files in this branch needs updating, or
  • the branch becomes conflicted, or
  • you click the rebase/retry checkbox if found above, or
  • you rename this PR's title to start with "rebase!" to trigger it manually

The artifact failure details are included below:

File name: uv.lock
Downloading cpython-3.10.22-linux-x86_64-gnu (download) (28.0MiB)
 Downloaded cpython-3.10.22-linux-x86_64-gnu (download)
Using CPython 3.10.22
error: No solution found when resolving dependencies for split (markers: python_full_version == '3.10.*' and platform_python_implementation != 'PyPy')
  cause: Because the requested Python version (>=3.10) does not satisfy Python>=3.11 and vcrpy==9.4.2 depends on Python>=3.11, we can conclude that vcrpy==9.4.2 cannot be used.
         And because only vcrpy<=9.4.2 is available, we can conclude that vcrpy>=9.4.2 cannot be used.
         And because picimagesearch:dev depends on vcrpy>=9.4.2 and your project requires picimagesearch:dev, we can conclude that your project's requirements are unsatisfiable.

hint: The `requires-python` value (>=3.10) includes Python versions that are not supported by your dependencies (e.g., vcrpy==9.4.2 only supports >=3.11). Consider using a more restrictive `requires-python` value (like >=3.11).

@renovate
renovate Bot force-pushed the renovate/vcrpy-9.x branch from dabd657 to cef6bb0 Compare November 1, 2026 00:49
@renovate
renovate Bot force-pushed the renovate/vcrpy-9.x branch from cef6bb0 to 290ee8b Compare November 3, 2026 00:39

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

breaking dependencies Pull requests that update a dependency file

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants