Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package python-vcrpy for openSUSE:Factory checked in at 2022-10-12 18:24:37 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-vcrpy (Old) and /work/SRC/openSUSE:Factory/.python-vcrpy.new.2275 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-vcrpy" Wed Oct 12 18:24:37 2022 rev:12 rq:1009893 version:4.2.1 Changes: -------- --- /work/SRC/openSUSE:Factory/python-vcrpy/python-vcrpy.changes 2021-08-27 21:44:33.758016181 +0200 +++ /work/SRC/openSUSE:Factory/.python-vcrpy.new.2275/python-vcrpy.changes 2022-10-12 18:26:15.397896566 +0200 @@ -1,0 +2,7 @@ +Tue Oct 11 16:48:12 UTC 2022 - Yogalakshmi Arunachalam <[email protected]> + +- Update to version 4.2.1 + * Fix a bug where the first request in a redirect chain was not being recorded with aiohttp + * Various typos and small fixes, thanks @jairhenrique, @timgates42 + +------------------------------------------------------------------- Old: ---- vcrpy-4.1.1.tar.gz New: ---- vcrpy-4.2.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-vcrpy.spec ++++++ --- /var/tmp/diff_new_pack.dhE713/_old 2022-10-12 18:26:15.937897755 +0200 +++ /var/tmp/diff_new_pack.dhE713/_new 2022-10-12 18:26:15.941897764 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-vcrpy # -# Copyright (c) 2021 SUSE LLC +# Copyright (c) 2022 SUSE LLC # Copyright (c) 2015 LISA GmbH, Bingen, Germany. # # All modifications and additions to the file contributed by third parties @@ -20,7 +20,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define skip_python2 1 Name: python-vcrpy -Version: 4.1.1 +Version: 4.2.1 Release: 0 Summary: Python module to mock and replay HTTP interactions License: MIT ++++++ vcrpy-4.1.1.tar.gz -> vcrpy-4.2.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/PKG-INFO new/vcrpy-4.2.1/PKG-INFO --- old/vcrpy-4.1.1/PKG-INFO 2020-10-09 22:41:12.000000000 +0200 +++ new/vcrpy-4.2.1/PKG-INFO 2022-08-31 21:15:46.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: vcrpy -Version: 4.1.1 +Version: 4.2.1 Summary: Automatically mock your HTTP interactions to simplify and speed up testing Home-page: https://github.com/kevin1024/vcrpy Author: Kevin McCarthy @@ -70,8 +70,8 @@ :target: https://pypi.python.org/pypi/vcrpy .. |Python versions| image:: https://img.shields.io/pypi/pyversions/vcrpy.svg :target: https://pypi.python.org/pypi/vcrpy - .. |Build Status| image:: https://secure.travis-ci.org/kevin1024/vcrpy.svg?branch=master - :target: http://travis-ci.org/kevin1024/vcrpy + .. |Build Status| image:: https://github.com/kevin1024/vcrpy/actions/workflows/main.yml/badge.svg + :target: https://github.com/kevin1024/vcrpy/actions .. |Gitter| image:: https://badges.gitter.im/Join%20Chat.svg :alt: Join the chat at https://gitter.im/kevin1024/vcrpy :target: https://gitter.im/kevin1024/vcrpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge @@ -88,15 +88,15 @@ Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Testing Classifier: Topic :: Internet :: WWW/HTTP Classifier: License :: OSI Approved :: MIT License -Requires-Python: >=3.5 +Requires-Python: >=3.7 Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/README.rst new/vcrpy-4.2.1/README.rst --- old/vcrpy-4.1.1/README.rst 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/README.rst 2022-08-31 21:12:36.000000000 +0200 @@ -62,8 +62,8 @@ :target: https://pypi.python.org/pypi/vcrpy .. |Python versions| image:: https://img.shields.io/pypi/pyversions/vcrpy.svg :target: https://pypi.python.org/pypi/vcrpy -.. |Build Status| image:: https://secure.travis-ci.org/kevin1024/vcrpy.svg?branch=master - :target: http://travis-ci.org/kevin1024/vcrpy +.. |Build Status| image:: https://github.com/kevin1024/vcrpy/actions/workflows/main.yml/badge.svg + :target: https://github.com/kevin1024/vcrpy/actions .. |Gitter| image:: https://badges.gitter.im/Join%20Chat.svg :alt: Join the chat at https://gitter.im/kevin1024/vcrpy :target: https://gitter.im/kevin1024/vcrpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/setup.py new/vcrpy-4.2.1/setup.py --- old/vcrpy-4.1.1/setup.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/setup.py 2022-08-31 21:12:36.000000000 +0200 @@ -46,8 +46,7 @@ "PyYAML", "wrapt", "six>=1.5", - 'yarl; python_version>="3.6"', - 'yarl<1.4; python_version=="3.5"', + "yarl", ] setup( @@ -60,7 +59,7 @@ author_email="[email protected]", url="https://github.com/kevin1024/vcrpy", packages=find_packages(exclude=["tests*"]), - python_requires=">=3.5", + python_requires=">=3.7", install_requires=install_requires, license="MIT", tests_require=["pytest", "mock", "pytest-httpbin"], @@ -70,10 +69,10 @@ "Intended Audience :: Developers", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: Implementation :: PyPy", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_aiohttp.py new/vcrpy-4.2.1/tests/integration/test_aiohttp.py --- old/vcrpy-4.1.1/tests/integration/test_aiohttp.py 2020-10-09 22:36:13.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_aiohttp.py 2022-08-31 21:12:36.000000000 +0200 @@ -152,12 +152,13 @@ def test_params(tmpdir, scheme): - url = scheme + "://httpbin.org/get" + url = scheme + "://httpbin.org/get?d=d" headers = {"Content-Type": "application/json"} - params = {"a": 1, "b": False, "c": "c"} + params = {"a": 1, "b": 2, "c": "c"} with vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette: _, response_json = get(url, output="json", params=params, headers=headers) + assert response_json["args"] == {"a": "1", "b": "2", "c": "c", "d": "d"} with vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette: _, cassette_response_json = get(url, output="json", params=params, headers=headers) @@ -168,7 +169,7 @@ def test_params_same_url_distinct_params(tmpdir, scheme): url = scheme + "://httpbin.org/get" headers = {"Content-Type": "application/json"} - params = {"a": 1, "b": False, "c": "c"} + params = {"a": 1, "b": 2, "c": "c"} with vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette: _, response_json = get(url, output="json", params=params, headers=headers) @@ -399,3 +400,19 @@ assert cookies["Cookie_1"].value == "Val_1" run_in_loop(run) + + +def test_not_allow_redirects(tmpdir): + url = "https://mockbin.org/redirect/308/5" + path = str(tmpdir.join("redirects.yaml")) + + with vcr.use_cassette(path): + response, _ = get(url, allow_redirects=False) + assert response.url.path == "/redirect/308/5" + assert response.status == 308 + + with vcr.use_cassette(path) as cassette: + response, _ = get(url, allow_redirects=False) + assert response.url.path == "/redirect/308/5" + assert response.status == 308 + assert cassette.play_count == 1 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_basic.py new/vcrpy-4.2.1/tests/integration/test_basic.py --- old/vcrpy-4.1.1/tests/integration/test_basic.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_basic.py 2022-08-31 21:12:36.000000000 +0200 @@ -11,7 +11,7 @@ def test_nonexistent_directory(tmpdir, httpbin): """If we load a cassette in a nonexistent directory, it can save ok""" - # Check to make sure directory doesnt exist + # Check to make sure directory doesn't exist assert not os.path.exists(str(tmpdir.join("nonexistent"))) # Run VCR to create dir and cassette file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_httplib2.py new/vcrpy-4.2.1/tests/integration/test_httplib2.py --- old/vcrpy-4.1.1/tests/integration/test_httplib2.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_httplib2.py 2022-08-31 21:12:36.000000000 +0200 @@ -61,13 +61,14 @@ assert set(headers) == set(resp.items()) -def test_effective_url(tmpdir, httpbin_both): +def test_effective_url(tmpdir): """Ensure that the effective_url is captured""" - url = httpbin_both.url + "/redirect-to?url=/html" + url = "http://mockbin.org/redirect/301" + with vcr.use_cassette(str(tmpdir.join("headers.yaml"))): resp, _ = http().request(url) effective_url = resp["content-location"] - assert effective_url == httpbin_both + "/html" + assert effective_url == "http://mockbin.org/redirect/301/0" with vcr.use_cassette(str(tmpdir.join("headers.yaml"))): resp, _ = http().request(url) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_httpx.py new/vcrpy-4.2.1/tests/integration/test_httpx.py --- old/vcrpy-4.1.1/tests/integration/test_httpx.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_httpx.py 2022-08-31 21:12:36.000000000 +0200 @@ -1,43 +1,79 @@ import pytest -import contextlib import os asyncio = pytest.importorskip("asyncio") httpx = pytest.importorskip("httpx") import vcr # noqa: E402 +from vcr.stubs.httpx_stubs import HTTPX_REDIRECT_PARAM # noqa: E402 class BaseDoRequest: _client_class = None def __init__(self, *args, **kwargs): - self._client = self._client_class(*args, **kwargs) + self._client_args = args + self._client_kwargs = kwargs + + def _make_client(self): + return self._client_class(*self._client_args, **self._client_kwargs) class DoSyncRequest(BaseDoRequest): _client_class = httpx.Client + def __enter__(self): + return self + + def __exit__(self, *args): + pass + + @property + def client(self): + try: + return self._client + except AttributeError: + self._client = self._make_client() + return self._client + def __call__(self, *args, **kwargs): - return self._client.request(*args, timeout=60, **kwargs) + return self.client.request(*args, timeout=60, **kwargs) class DoAsyncRequest(BaseDoRequest): _client_class = httpx.AsyncClient - @staticmethod - def run_in_loop(coroutine): - with contextlib.closing(asyncio.new_event_loop()) as loop: - asyncio.set_event_loop(loop) - task = loop.create_task(coroutine) - return loop.run_until_complete(task) + def __enter__(self): + # Need to manage both loop and client, because client's implementation + # will fail if the loop is closed before the client's end of life. + self._loop = asyncio.new_event_loop() + asyncio.set_event_loop(self._loop) + self._client = self._make_client() + self._loop.run_until_complete(self._client.__aenter__()) + return self + + def __exit__(self, *args): + try: + self._loop.run_until_complete(self._client.__aexit__(*args)) + finally: + del self._client + self._loop.close() + del self._loop + + @property + def client(self): + try: + return self._client + except AttributeError: + raise ValueError('To access async client, use "with do_request() as client"') def __call__(self, *args, **kwargs): - async def _request(): - async with self._client as c: - return await c.request(*args, **kwargs) + if hasattr(self, "_loop"): + return self._loop.run_until_complete(self.client.request(*args, **kwargs)) - return DoAsyncRequest.run_in_loop(_request()) + # Use one-time context and dispose of the loop/client afterwards + with self: + return self(*args, **kwargs) def pytest_generate_tests(metafunc): @@ -122,12 +158,14 @@ def test_redirect(tmpdir, do_request, yml): url = "https://mockbin.org/redirect/303/2" - response = do_request()("GET", url) + redirect_kwargs = {HTTPX_REDIRECT_PARAM.name: True} + + response = do_request()("GET", url, **redirect_kwargs) with vcr.use_cassette(yml): - response = do_request()("GET", url) + response = do_request()("GET", url, **redirect_kwargs) with vcr.use_cassette(yml) as cassette: - cassette_response = do_request()("GET", url) + cassette_response = do_request()("GET", url, **redirect_kwargs) assert cassette_response.status_code == response.status_code assert len(cassette_response.history) == len(response.history) @@ -173,7 +211,7 @@ ) url = "https://httpbin.org/headers" proxy = "http://localhost:8080" - proxies = {"http": proxy, "https": proxy} + proxies = {"http://": proxy, "https://": proxy} with vcr.use_cassette(yml): response = do_request(proxies=proxies, verify=False)("GET", url) @@ -189,48 +227,52 @@ def test_cookies(tmpdir, scheme, do_request): def client_cookies(client): - return [c for c in client._client.cookies] + return [c for c in client.client.cookies] def response_cookies(response): return [c for c in response.cookies] - client = do_request() - assert client_cookies(client) == [] + with do_request() as client: + assert client_cookies(client) == [] - url = scheme + "://httpbin.org" - testfile = str(tmpdir.join("cookies.yml")) - with vcr.use_cassette(testfile): - r1 = client("GET", url + "/cookies/set?k1=v1&k2=v2") - assert response_cookies(r1.history[0]) == ["k1", "k2"] - assert response_cookies(r1) == [] + redirect_kwargs = {HTTPX_REDIRECT_PARAM.name: True} - r2 = client("GET", url + "/cookies") - assert len(r2.json()["cookies"]) == 2 + url = scheme + "://httpbin.org" + testfile = str(tmpdir.join("cookies.yml")) + with vcr.use_cassette(testfile): + r1 = client("GET", url + "/cookies/set?k1=v1&k2=v2", **redirect_kwargs) + assert response_cookies(r1.history[0]) == ["k1", "k2"] + assert response_cookies(r1) == [] - assert client_cookies(client) == ["k1", "k2"] + r2 = client("GET", url + "/cookies", **redirect_kwargs) + assert len(r2.json()["cookies"]) == 2 - new_client = do_request() - assert client_cookies(new_client) == [] + assert client_cookies(client) == ["k1", "k2"] - with vcr.use_cassette(testfile) as cassette: - cassette_response = new_client("GET", url + "/cookies/set?k1=v1&k2=v2") - assert response_cookies(cassette_response.history[0]) == ["k1", "k2"] - assert response_cookies(cassette_response) == [] + with do_request() as new_client: + assert client_cookies(new_client) == [] - assert cassette.play_count == 2 - assert client_cookies(new_client) == ["k1", "k2"] + with vcr.use_cassette(testfile) as cassette: + cassette_response = new_client("GET", url + "/cookies/set?k1=v1&k2=v2") + assert response_cookies(cassette_response.history[0]) == ["k1", "k2"] + assert response_cookies(cassette_response) == [] + + assert cassette.play_count == 2 + assert client_cookies(new_client) == ["k1", "k2"] def test_relative_redirects(tmpdir, scheme, do_request): + redirect_kwargs = {HTTPX_REDIRECT_PARAM.name: True} + url = scheme + "://mockbin.com/redirect/301?to=/redirect/301?to=/request" testfile = str(tmpdir.join("relative_redirects.yml")) with vcr.use_cassette(testfile): - response = do_request()("GET", url) + response = do_request()("GET", url, **redirect_kwargs) assert len(response.history) == 2, response assert response.json()["url"].endswith("request") with vcr.use_cassette(testfile) as cassette: - response = do_request()("GET", url) + response = do_request()("GET", url, **redirect_kwargs) assert len(response.history) == 2 assert response.json()["url"].endswith("request") @@ -240,14 +282,16 @@ def test_redirect_wo_allow_redirects(do_request, yml): url = "https://mockbin.org/redirect/308/5" + redirect_kwargs = {HTTPX_REDIRECT_PARAM.name: False} + with vcr.use_cassette(yml): - response = do_request()("GET", url, allow_redirects=False) + response = do_request()("GET", url, **redirect_kwargs) assert str(response.url).endswith("308/5") assert response.status_code == 308 with vcr.use_cassette(yml) as cassette: - response = do_request()("GET", url, allow_redirects=False) + response = do_request()("GET", url, **redirect_kwargs) assert str(response.url).endswith("308/5") assert response.status_code == 308 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_record_mode.py new/vcrpy-4.2.1/tests/integration/test_record_mode.py --- old/vcrpy-4.1.1/tests/integration/test_record_mode.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_record_mode.py 2022-08-31 21:12:36.000000000 +0200 @@ -57,7 +57,7 @@ assert cass.all_played # in the "new_episodes" record mode, we can add more requests to - # a cassette without repurcussions. + # a cassette without repercussions. urlopen(httpbin.url + "/get").read() # one of the responses has been played @@ -108,7 +108,7 @@ urlopen(httpbin.url).read() # in the "all" record mode, we can add more requests to - # a cassette without repurcussions. + # a cassette without repercussions. urlopen(httpbin.url + "/get").read() # The cassette was never actually played, even though it existed. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_register_persister.py new/vcrpy-4.2.1/tests/integration/test_register_persister.py --- old/vcrpy-4.1.1/tests/integration/test_register_persister.py 2020-10-09 22:36:13.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_register_persister.py 2022-08-31 21:12:36.000000000 +0200 @@ -30,7 +30,7 @@ my_vcr = vcr.VCR() my_vcr.register_persister(CustomFilesystemPersister) - # Check to make sure directory doesnt exist + # Check to make sure directory doesn't exist assert not os.path.exists(str(tmpdir.join("nonexistent"))) # Run VCR to create dir and cassette file using new save_cassette callback diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_requests.py new/vcrpy-4.2.1/tests/integration/test_requests.py --- old/vcrpy-4.1.1/tests/integration/test_requests.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_requests.py 2022-08-31 21:12:36.000000000 +0200 @@ -1,8 +1,5 @@ -# -*- coding: utf-8 -*- """Test requests' interaction with vcr""" -import platform import pytest -import sys import vcr from assertions import assert_cassette_empty, assert_is_json @@ -117,10 +114,6 @@ @pytest.mark.skipif("sys.version_info >= (3, 6)", strict=True, raises=ConnectionError) [email protected]( - (3, 5) < sys.version_info < (3, 6) and platform.python_implementation() == "CPython", - reason="Fails on CPython 3.5", -) def test_post_chunked_binary_secure(tmpdir, httpbin_secure): """Ensure that we can send chunked binary without breaking while trying to concatenate bytes with str.""" data1 = iter([b"data", b"to", b"send"]) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_stubs.py new/vcrpy-4.2.1/tests/integration/test_stubs.py --- old/vcrpy-4.1.1/tests/integration/test_stubs.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_stubs.py 2022-08-31 21:12:36.000000000 +0200 @@ -64,7 +64,7 @@ inside = conn.getresponse() # Assert that we do not modify the original response while appending - # to the casssette. + # to the cassette. assert "gzip" == inside.headers["content-encoding"] # They should effectively be the same response. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_urllib2.py new/vcrpy-4.2.1/tests/integration/test_urllib2.py --- old/vcrpy-4.1.1/tests/integration/test_urllib2.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_urllib2.py 2022-08-31 21:12:36.000000000 +0200 @@ -56,12 +56,13 @@ assert sorted(open1) == sorted(open2) -def test_effective_url(httpbin_both, tmpdir): +def test_effective_url(tmpdir): """Ensure that the effective_url is captured""" - url = httpbin_both.url + "/redirect-to?url=/html" + url = "http://mockbin.org/redirect/301" + with vcr.use_cassette(str(tmpdir.join("headers.yaml"))): effective_url = urlopen_with_cafile(url).geturl() - assert effective_url == httpbin_both.url + "/html" + assert effective_url == "http://mockbin.org/redirect/301/0" with vcr.use_cassette(str(tmpdir.join("headers.yaml"))): assert effective_url == urlopen_with_cafile(url).geturl() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/integration/test_urllib3.py new/vcrpy-4.2.1/tests/integration/test_urllib3.py --- old/vcrpy-4.1.1/tests/integration/test_urllib3.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/integration/test_urllib3.py 2022-08-31 21:12:36.000000000 +0200 @@ -94,9 +94,10 @@ assert req1 == req2 -def test_redirects(tmpdir, httpbin_both, verify_pool_mgr): +def test_redirects(tmpdir, verify_pool_mgr): """Ensure that we can handle redirects""" - url = httpbin_both.url + "/redirect-to?url=bytes/1024" + url = "http://mockbin.org/redirect/301" + with vcr.use_cassette(str(tmpdir.join("verify_pool_mgr.yaml"))): content = verify_pool_mgr.request("GET", url).data @@ -104,8 +105,9 @@ assert content == verify_pool_mgr.request("GET", url).data # Ensure that we've now cached *two* responses. One for the redirect # and one for the final fetch - assert len(cass) == 2 - assert cass.play_count == 2 + + assert len(cass) == 2 + assert cass.play_count == 2 def test_cross_scheme(tmpdir, httpbin, httpbin_secure, verify_pool_mgr): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/unit/test_cassettes.py new/vcrpy-4.2.1/tests/unit/test_cassettes.py --- old/vcrpy-4.1.1/tests/unit/test_cassettes.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/unit/test_cassettes.py 2022-08-31 21:12:36.000000000 +0200 @@ -208,7 +208,7 @@ ) assert_get_response_body_is("first_response") - # Make sure a second cassette can supercede the first + # Make sure a second cassette can supersede the first with Cassette.use(path="test") as second_cassette: with mock.patch.object(second_cassette, "play_response", return_value=second_response): assert_get_response_body_is("second_response") @@ -310,16 +310,16 @@ def test_use_as_decorator_on_coroutine(): - original_http_connetion = httplib.HTTPConnection + original_http_connection = httplib.HTTPConnection @Cassette.use(inject=True) def test_function(cassette): assert httplib.HTTPConnection.cassette is cassette - assert httplib.HTTPConnection is not original_http_connetion + assert httplib.HTTPConnection is not original_http_connection value = yield 1 assert value == 1 assert httplib.HTTPConnection.cassette is cassette - assert httplib.HTTPConnection is not original_http_connetion + assert httplib.HTTPConnection is not original_http_connection value = yield 2 assert value == 2 @@ -333,15 +333,15 @@ def test_use_as_decorator_on_generator(): - original_http_connetion = httplib.HTTPConnection + original_http_connection = httplib.HTTPConnection @Cassette.use(inject=True) def test_function(cassette): assert httplib.HTTPConnection.cassette is cassette - assert httplib.HTTPConnection is not original_http_connetion + assert httplib.HTTPConnection is not original_http_connection yield 1 assert httplib.HTTPConnection.cassette is cassette - assert httplib.HTTPConnection is not original_http_connetion + assert httplib.HTTPConnection is not original_http_connection yield 2 assert list(test_function()) == [1, 2] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/unit/test_filters.py new/vcrpy-4.2.1/tests/unit/test_filters.py --- old/vcrpy-4.1.1/tests/unit/test_filters.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/unit/test_filters.py 2022-08-31 21:12:36.000000000 +0200 @@ -220,6 +220,49 @@ assert request.body == b"{}" +def test_replace_dict_post_data_parameters(): + # This tests all of: + # 1. keeping a parameter + # 2. removing a parameter + # 3. replacing a parameter + # 4. replacing a parameter using a callable + # 5. removing a parameter using a callable + # 6. replacing a parameter that doesn't exist + body = {"one": "keep", "two": "lose", "three": "change", "four": "shout", "five": "whisper"} + request = Request("POST", "http://google.com", body, {}) + request.headers["Content-Type"] = "application/x-www-form-urlencoded" + replace_post_data_parameters( + request, + [ + ("two", None), + ("three", "tada"), + ("four", lambda key, value, request: value.upper()), + ("five", lambda key, value, request: None), + ("six", "doesntexist"), + ], + ) + expected_data = {"one": "keep", "three": "tada", "four": "SHOUT"} + assert request.body == expected_data + + +def test_remove_dict_post_data_parameters(): + # Test the backward-compatible API wrapper. + body = {"id": "secret", "foo": "bar", "baz": "qux"} + request = Request("POST", "http://google.com", body, {}) + request.headers["Content-Type"] = "application/x-www-form-urlencoded" + remove_post_data_parameters(request, ["id"]) + expected_data = {"foo": "bar", "baz": "qux"} + assert request.body == expected_data + + +def test_remove_all_dict_post_data_parameters(): + body = {"id": "secret", "foo": "bar"} + request = Request("POST", "http://google.com", body, {}) + request.headers["Content-Type"] = "application/x-www-form-urlencoded" + replace_post_data_parameters(request, [("id", None), ("foo", None)]) + assert request.body == {} + + def test_decode_response_uncompressed(): recorded_response = { "status": {"message": "OK", "code": 200}, diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/unit/test_migration.py new/vcrpy-4.2.1/tests/unit/test_migration.py --- old/vcrpy-4.1.1/tests/unit/test_migration.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/unit/test_migration.py 2022-08-31 21:12:36.000000000 +0200 @@ -44,4 +44,4 @@ for file_path in files: shutil.copy(file_path, cassette) assert not vcr.migration.try_migrate(cassette) - assert filecmp.cmp(cassette, file_path) # shold not change file + assert filecmp.cmp(cassette, file_path) # should not change file diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/unit/test_stubs.py new/vcrpy-4.2.1/tests/unit/test_stubs.py --- old/vcrpy-4.1.1/tests/unit/test_stubs.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/unit/test_stubs.py 2022-08-31 21:12:36.000000000 +0200 @@ -6,7 +6,7 @@ class TestVCRConnection: - def test_setting_of_attributes_get_propogated_to_real_connection(self): + def test_setting_of_attributes_get_propagated_to_real_connection(self): vcr_connection = VCRHTTPSConnection("www.examplehost.com") vcr_connection.ssl_version = "example_ssl_version" assert vcr_connection.real_connection.ssl_version == "example_ssl_version" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tests/unit/test_vcr.py new/vcrpy-4.2.1/tests/unit/test_vcr.py --- old/vcrpy-4.1.1/tests/unit/test_vcr.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/tests/unit/test_vcr.py 2022-08-31 21:12:36.000000000 +0200 @@ -31,7 +31,7 @@ function() assert mock_cassette_load.call_args[1]["record_mode"] == test_vcr.record_mode - # Ensure that explicitly provided arguments still supercede + # Ensure that explicitly provided arguments still supersede # those on the vcr. new_record_mode = mock.Mock() @@ -226,7 +226,7 @@ def test_cassette_library_dir_with_decoration_and_no_explicit_path(): - library_dir = "/libary_dir" + library_dir = "/library_dir" vcr = VCR(inject_cassette=True, cassette_library_dir=library_dir) @vcr.use_cassette() @@ -237,7 +237,7 @@ def test_cassette_library_dir_with_decoration_and_explicit_path(): - library_dir = "/libary_dir" + library_dir = "/library_dir" vcr = VCR(inject_cassette=True, cassette_library_dir=library_dir) @vcr.use_cassette(path="custom_name") @@ -248,7 +248,7 @@ def test_cassette_library_dir_with_decoration_and_super_explicit_path(): - library_dir = "/libary_dir" + library_dir = "/library_dir" vcr = VCR(inject_cassette=True, cassette_library_dir=library_dir) @vcr.use_cassette(path=os.path.join(library_dir, "custom_name")) @@ -259,7 +259,7 @@ def test_cassette_library_dir_with_path_transformer(): - library_dir = "/libary_dir" + library_dir = "/library_dir" vcr = VCR( inject_cassette=True, cassette_library_dir=library_dir, path_transformer=lambda path: path + ".json" ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/tox.ini new/vcrpy-4.2.1/tox.ini --- old/vcrpy-4.1.1/tox.ini 2020-10-09 22:36:13.000000000 +0200 +++ new/vcrpy-4.2.1/tox.ini 2022-08-31 21:12:36.000000000 +0200 @@ -3,12 +3,20 @@ envlist = cov-clean, lint, - {py35,py36,py37,py38}-{requests,httplib2,urllib3,tornado4,boto3,aiohttp}, - {py36,py37,py38}-{httpx} + {py37,py38,py39,py310}-{requests,httplib2,urllib3,tornado4,boto3,aiohttp,httpx}, {pypy3}-{requests,httplib2,urllib3,tornado4,boto3}, + {py310}-httpx019, cov-report +[gh-actions] +python = + 3.7: py37, lint + 3.8: py38 + 3.9: py39 + 3.10: py310 + pypy-3: pypy3 + # Coverage environment tasks: cov-clean and cov-report # https://pytest-cov.readthedocs.io/en/latest/tox.html [testenv:cov-clean] @@ -34,6 +42,7 @@ deps = flake8 black +basepython = python3.7 [testenv:docs] # Running sphinx from inside the "docs" directory @@ -63,29 +72,30 @@ commands = ./runtests.sh --cov=./vcr --cov-branch --cov-report=xml --cov-append {posargs} deps = - Flask + Werkzeug==2.0.3 pytest - pytest-httpbin + git+https://github.com/immerrr/pytest-httpbin@fix-redirect-location-scheme-for-secure-server pytest-cov PyYAML ipaddress requests: requests>=2.22.0 httplib2: httplib2 urllib3: urllib3 - {py35,py36}-tornado4: tornado>=4,<5 - {py35,py36}-tornado4: pytest-tornado - {py35,py36}-tornado4: pycurl boto3: boto3 boto3: urllib3 aiohttp: aiohttp aiohttp: pytest-asyncio aiohttp: pytest-aiohttp httpx: httpx - {py36,py37,py38}-{httpx}: httpx - {py36,py37,py38}-{httpx}: pytest-asyncio + {py37,py38,py39,py310}-{httpx}: httpx + {py37,py38,py39,py310}-{httpx}: pytest-asyncio + httpx: httpx>0.19 + # httpx==0.19 is the latest version that supports allow_redirects, newer versions use follow_redirects + httpx019: httpx==0.19 + {py37,py38,py39,py310}-{httpx}: pytest-asyncio depends = - lint,{py35,py36,py37,py38,pypy3}-{requests,httplib2,urllib3,tornado4,boto3},{py35,py36,py37,py38}-{aiohttp},{py36,py37,py38}-{httpx}: cov-clean - cov-report: lint,{py35,py36,py37,py38,pypy3}-{requests,httplib2,urllib3,tornado4,boto3},{py35,py36,py37,py38}-{aiohttp} + lint,{py37,py38,py39,py310,pypy3}-{requests,httplib2,urllib3,tornado4,boto3},{py37,py38,py39,py310}-{aiohttp},{py37,py38,py39,py310}-{httpx}: cov-clean + cov-report: lint,{py37,py38,py39,py310,pypy3}-{requests,httplib2,urllib3,tornado4,boto3},{py37,py38,py39,py310}-{aiohttp} passenv = AWS_ACCESS_KEY_ID AWS_DEFAULT_REGION diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcr/__init__.py new/vcrpy-4.2.1/vcr/__init__.py --- old/vcrpy-4.1.1/vcr/__init__.py 2020-10-09 22:38:26.000000000 +0200 +++ new/vcrpy-4.2.1/vcr/__init__.py 2022-08-31 21:14:17.000000000 +0200 @@ -3,7 +3,7 @@ from logging import NullHandler from .record_mode import RecordMode as mode # noqa import is not used in this file -__version__ = "4.1.1" +__version__ = "4.2.1" logging.getLogger(__name__).addHandler(NullHandler()) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcr/filters.py new/vcrpy-4.2.1/vcr/filters.py --- old/vcrpy-4.1.1/vcr/filters.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/vcr/filters.py 2022-08-31 21:12:36.000000000 +0200 @@ -84,7 +84,17 @@ replacements = dict(replacements) if request.method == "POST" and not isinstance(request.body, BytesIO): - if request.headers.get("Content-Type") == "application/json": + if isinstance(request.body, dict): + new_body = request.body.copy() + for k, rv in replacements.items(): + if k in new_body: + ov = new_body.pop(k) + if callable(rv): + rv = rv(key=k, value=ov, request=request) + if rv is not None: + new_body[k] = rv + request.body = new_body + elif request.headers.get("Content-Type") == "application/json": json_data = json.loads(request.body.decode("utf-8")) for k, rv in replacements.items(): if k in json_data: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcr/record_mode.py new/vcrpy-4.2.1/vcr/record_mode.py --- old/vcrpy-4.1.1/vcr/record_mode.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/vcr/record_mode.py 2022-08-31 21:12:36.000000000 +0200 @@ -3,7 +3,7 @@ class RecordMode(str, Enum): """ - Configues when VCR will record to the cassette. + Configures when VCR will record to the cassette. Can be declared by either using the enumerated value (`vcr.mode.ONCE`) or by simply using the defined string (`once`). diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcr/stubs/__init__.py new/vcrpy-4.2.1/vcr/stubs/__init__.py --- old/vcrpy-4.1.1/vcr/stubs/__init__.py 2020-07-31 16:51:06.000000000 +0200 +++ new/vcrpy-4.2.1/vcr/stubs/__init__.py 2022-08-31 21:12:36.000000000 +0200 @@ -87,7 +87,7 @@ def closed(self): # in python3, I can't change the value of self.closed. So I' # twiddling self._closed and using this property to shadow the real - # self.closed from the superclas + # self.closed from the superclass return self._closed def read(self, *args, **kwargs): @@ -314,7 +314,7 @@ def __setattr__(self, name, value): """ We need to define this because any attributes that are set on the - VCRConnection need to be propogated to the real connection. + VCRConnection need to be propagated to the real connection. For example, urllib3 will set certain attributes on the connection, such as 'ssl_version'. These attributes need to get set on the real diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcr/stubs/aiohttp_stubs.py new/vcrpy-4.2.1/vcr/stubs/aiohttp_stubs.py --- old/vcrpy-4.1.1/vcr/stubs/aiohttp_stubs.py 2020-10-09 22:36:13.000000000 +0200 +++ new/vcrpy-4.2.1/vcr/stubs/aiohttp_stubs.py 2022-08-31 21:12:36.000000000 +0200 @@ -8,7 +8,8 @@ from aiohttp import hdrs, CookieJar from http.cookies import CookieError, Morsel, SimpleCookie from aiohttp.helpers import strip_auth_from_url -from multidict import CIMultiDict, CIMultiDictProxy +from multidict import CIMultiDict, CIMultiDictProxy, MultiDict +from typing import Union, Mapping from yarl import URL from vcr.errors import CannotOverwriteExistingCassetteException @@ -116,14 +117,15 @@ return CIMultiDictProxy(deserialized_headers) -def play_responses(cassette, vcr_request): +def play_responses(cassette, vcr_request, kwargs): history = [] + allow_redirects = kwargs.get("allow_redirects", True) vcr_response = cassette.play_response(vcr_request) response = build_response(vcr_request, vcr_response, history) # If we're following redirects, continue playing until we reach # our final destination. - while 300 <= response.status <= 399: + while allow_redirects and 300 <= response.status <= 399: if "location" not in response.headers: break @@ -175,14 +177,14 @@ to the final destination. """ - for past_response in response.history: + for i, past_response in enumerate(response.history): aiohttp_request = past_response.request_info - # No data because it's following a redirect. past_request = Request( aiohttp_request.method, str(aiohttp_request.url), - None, + # Record body of first request, rest are following a redirect. + None if i else vcr_request.body, _serialize_headers(aiohttp_request.headers), ) await record_response(cassette, past_request, past_response) @@ -228,6 +230,16 @@ return c.output(header="", sep=";").strip() +def _build_url_with_params(url_str: str, params: Mapping[str, Union[str, int, float]]) -> URL: + # This code is basically a copy&paste of aiohttp. + # https://github.com/aio-libs/aiohttp/blob/master/aiohttp/client_reqrep.py#L225 + url = URL(url_str) + q = MultiDict(url.query) + url2 = url.with_query(params) + q.extend(url2.query) + return url.with_query(q) + + def vcr_request(cassette, real_request): @functools.wraps(real_request) async def new_request(self, method, url, **kwargs): @@ -241,12 +253,7 @@ if auth is not None: headers["AUTHORIZATION"] = auth.encode() - request_url = URL(url) - if params: - for k, v in params.items(): - params[k] = str(v) - request_url = URL(url).with_query(params) - + request_url = URL(url) if not params else _build_url_with_params(url, params) c_header = headers.pop(hdrs.COOKIE, None) cookie_header = _build_cookie_header(self, cookies, c_header, request_url) if cookie_header: @@ -256,7 +263,7 @@ if cassette.can_play_response_for(vcr_request): log.info("Playing response for {} from cassette".format(vcr_request)) - response = play_responses(cassette, vcr_request) + response = play_responses(cassette, vcr_request, kwargs) for redirect in response.history: self._cookie_jar.update_cookies(redirect.cookies, redirect.url) self._cookie_jar.update_cookies(response.cookies, response.url) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcr/stubs/httpx_stubs.py new/vcrpy-4.2.1/vcr/stubs/httpx_stubs.py --- old/vcrpy-4.1.1/vcr/stubs/httpx_stubs.py 2020-10-09 22:36:13.000000000 +0200 +++ new/vcrpy-4.2.1/vcr/stubs/httpx_stubs.py 2022-08-31 21:12:36.000000000 +0200 @@ -5,31 +5,39 @@ import httpx from vcr.request import Request as VcrRequest from vcr.errors import CannotOverwriteExistingCassetteException +import inspect + +_httpx_signature = inspect.signature(httpx.Client.request) + +try: + HTTPX_REDIRECT_PARAM = _httpx_signature.parameters["follow_redirects"] +except KeyError: + HTTPX_REDIRECT_PARAM = _httpx_signature.parameters["allow_redirects"] _logger = logging.getLogger(__name__) -def _transform_headers(httpx_reponse): +def _transform_headers(httpx_response): """ Some headers can appear multiple times, like "Set-Cookie". Therefore transform to every header key to list of values. """ out = {} - for key, var in httpx_reponse.headers.raw: + for key, var in httpx_response.headers.raw: decoded_key = key.decode("utf-8") out.setdefault(decoded_key, []) out[decoded_key].append(var.decode("utf-8")) return out -def _to_serialized_response(httpx_reponse): +def _to_serialized_response(httpx_response): return { - "status_code": httpx_reponse.status_code, - "http_version": httpx_reponse.http_version, - "headers": _transform_headers(httpx_reponse), - "content": httpx_reponse.content.decode("utf-8", "ignore"), + "status_code": httpx_response.status_code, + "http_version": httpx_response.http_version, + "headers": _transform_headers(httpx_response), + "content": httpx_response.content.decode("utf-8", "ignore"), } @@ -98,7 +106,11 @@ def _play_responses(cassette, request, vcr_request, client, kwargs): history = [] - allow_redirects = kwargs.get("allow_redirects", True) + + allow_redirects = kwargs.get( + HTTPX_REDIRECT_PARAM.name, + HTTPX_REDIRECT_PARAM.default, + ) vcr_response = cassette.play_response(vcr_request) response = _from_serialized_response(request, vcr_response) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcrpy.egg-info/PKG-INFO new/vcrpy-4.2.1/vcrpy.egg-info/PKG-INFO --- old/vcrpy-4.1.1/vcrpy.egg-info/PKG-INFO 2020-10-09 22:41:12.000000000 +0200 +++ new/vcrpy-4.2.1/vcrpy.egg-info/PKG-INFO 2022-08-31 21:15:46.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 2.1 Name: vcrpy -Version: 4.1.1 +Version: 4.2.1 Summary: Automatically mock your HTTP interactions to simplify and speed up testing Home-page: https://github.com/kevin1024/vcrpy Author: Kevin McCarthy @@ -70,8 +70,8 @@ :target: https://pypi.python.org/pypi/vcrpy .. |Python versions| image:: https://img.shields.io/pypi/pyversions/vcrpy.svg :target: https://pypi.python.org/pypi/vcrpy - .. |Build Status| image:: https://secure.travis-ci.org/kevin1024/vcrpy.svg?branch=master - :target: http://travis-ci.org/kevin1024/vcrpy + .. |Build Status| image:: https://github.com/kevin1024/vcrpy/actions/workflows/main.yml/badge.svg + :target: https://github.com/kevin1024/vcrpy/actions .. |Gitter| image:: https://badges.gitter.im/Join%20Chat.svg :alt: Join the chat at https://gitter.im/kevin1024/vcrpy :target: https://gitter.im/kevin1024/vcrpy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge @@ -88,15 +88,15 @@ Classifier: Intended Audience :: Developers Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.5 -Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Testing Classifier: Topic :: Internet :: WWW/HTTP Classifier: License :: OSI Approved :: MIT License -Requires-Python: >=3.5 +Requires-Python: >=3.7 Description-Content-Type: text/x-rst diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/vcrpy-4.1.1/vcrpy.egg-info/requires.txt new/vcrpy-4.2.1/vcrpy.egg-info/requires.txt --- old/vcrpy-4.1.1/vcrpy.egg-info/requires.txt 2020-10-09 22:41:12.000000000 +0200 +++ new/vcrpy-4.2.1/vcrpy.egg-info/requires.txt 2022-08-31 21:15:46.000000000 +0200 @@ -1,9 +1,4 @@ PyYAML wrapt six>=1.5 - -[:python_version == "3.5"] -yarl<1.4 - -[:python_version >= "3.6"] yarl
