diff --git a/pyproject.toml b/pyproject.toml index c69082d..a4eecff 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -25,6 +25,7 @@ select = [ "RUF", # Ruff-specific rules "UP", # pyupgrade "W", # pycodestyle warning + "SIM", ] [tool.ruff.lint.isort] diff --git a/setup.py b/setup.py index cb55cdf..4430478 100644 --- a/setup.py +++ b/setup.py @@ -3,10 +3,11 @@ import codecs import os import re +from pathlib import Path from setuptools import find_packages, setup -long_description = open("README.rst").read() +long_description = Path("README.rst").read_text() here = os.path.abspath(os.path.dirname(__file__)) @@ -29,16 +30,17 @@ def find_version(*file_paths): install_requires = [ "PyYAML", "wrapt", - "yarl", ] extras_require = { "tests": [ "aiohttp", "boto3", - "httplib2", + "cryptography", "httpbin", + "httplib2", "httpx", + "pycurl; platform_python_implementation !='PyPy'", "pytest", "pytest-aiohttp", "pytest-asyncio", @@ -47,6 +49,7 @@ extras_require = { "requests>=2.22.0", "tornado", "urllib3", + "werkzeug==2.0.3", ], } diff --git a/tests/integration/test_aiohttp.py b/tests/integration/test_aiohttp.py index eaa2fd1..dfcc5ab 100644 --- a/tests/integration/test_aiohttp.py +++ b/tests/integration/test_aiohttp.py @@ -193,9 +193,11 @@ def test_params_same_url_distinct_params(tmpdir, httpbin): assert cassette.play_count == 1 other_params = {"other": "params"} - with vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette: - with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException): - get(url, output="text", params=other_params) + with ( + vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette, + pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException), + ): + get(url, output="text", params=other_params) @pytest.mark.online diff --git a/tests/integration/test_config.py b/tests/integration/test_config.py index efe469a..3d827da 100644 --- a/tests/integration/test_config.py +++ b/tests/integration/test_config.py @@ -62,9 +62,8 @@ def test_override_match_on(tmpdir, httpbin): def test_missing_matcher(): my_vcr = vcr.VCR() my_vcr.register_matcher("awesome", object) - with pytest.raises(KeyError): - with my_vcr.use_cassette("test.yaml", match_on=["notawesome"]): - pass + with pytest.raises(KeyError), my_vcr.use_cassette("test.yaml", match_on=["notawesome"]): + pass @pytest.mark.online @@ -81,9 +80,8 @@ def test_dont_record_on_exception(tmpdir, httpbin): assert not os.path.exists(str(tmpdir.join("dontsave.yml"))) # Make sure context decorator has the same behavior - with pytest.raises(AssertionError): - with my_vcr.use_cassette(str(tmpdir.join("dontsave2.yml"))): - assert b"Not in content" in urlopen(httpbin.url).read() + with pytest.raises(AssertionError), my_vcr.use_cassette(str(tmpdir.join("dontsave2.yml"))): + assert b"Not in content" in urlopen(httpbin.url).read() assert not os.path.exists(str(tmpdir.join("dontsave2.yml"))) diff --git a/tests/integration/test_httpx.py b/tests/integration/test_httpx.py index 54e052c..841ce30 100644 --- a/tests/integration/test_httpx.py +++ b/tests/integration/test_httpx.py @@ -60,9 +60,8 @@ class DoSyncRequest(BaseDoRequest): return b"".join(response.iter_bytes()) # Use one-time context and dispose of the client afterwards - with self: - with self.client.stream(*args, **kwargs) as response: - return b"".join(response.iter_bytes()) + with self, self.client.stream(*args, **kwargs) as response: + return b"".join(response.iter_bytes()) class DoAsyncRequest(BaseDoRequest): @@ -195,9 +194,11 @@ def test_params_same_url_distinct_params(tmpdir, httpbin, do_request): assert cassette.play_count == 1 params = {"other": "params"} - with vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette: - with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException): - do_request()("GET", url, params=params, headers=headers) + with ( + vcr.use_cassette(str(tmpdir.join("get.yaml"))) as cassette, + pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException), + ): + do_request()("GET", url, params=params, headers=headers) @pytest.mark.online diff --git a/tests/integration/test_matchers.py b/tests/integration/test_matchers.py index ec4a703..1524ad3 100644 --- a/tests/integration/test_matchers.py +++ b/tests/integration/test_matchers.py @@ -51,9 +51,11 @@ def test_matchers(httpbin, httpbin_secure, cassette, matcher, matching_uri, not_ assert cass.play_count == 1 # play cassette with not matching on uri, it should fail - with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException): - with vcr.use_cassette(cassette, match_on=[matcher]) as cass: - urlopen(not_matching_uri) + with ( + pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException), + vcr.use_cassette(cassette, match_on=[matcher]) as cass, + ): + urlopen(not_matching_uri) def test_method_matcher(cassette, httpbin, httpbin_secure): @@ -65,10 +67,12 @@ def test_method_matcher(cassette, httpbin, httpbin_secure): assert cass.play_count == 1 # should fail if method does not match - with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException): - with vcr.use_cassette(cassette, match_on=["method"]) as cass: - # is a POST request - urlopen(default_uri, data=b"") + with ( + pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException), + vcr.use_cassette(cassette, match_on=["method"]) as cass, + ): + # is a POST request + urlopen(default_uri, data=b"") @pytest.mark.parametrize( @@ -98,14 +102,12 @@ def test_default_matcher_matches(cassette, uri, httpbin, httpbin_secure): ) def test_default_matcher_does_not_match(cassette, uri, httpbin, httpbin_secure): uri = _replace_httpbin(uri, httpbin, httpbin_secure) - with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException): - with vcr.use_cassette(cassette): - urlopen(uri) + with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException), vcr.use_cassette(cassette): + urlopen(uri) def test_default_matcher_does_not_match_on_method(cassette, httpbin, httpbin_secure): default_uri = _replace_httpbin(DEFAULT_URI, httpbin, httpbin_secure) - with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException): - with vcr.use_cassette(cassette): - # is a POST request - urlopen(default_uri, data=b"") + with pytest.raises(vcr.errors.CannotOverwriteExistingCassetteException), vcr.use_cassette(cassette): + # is a POST request + urlopen(default_uri, data=b"") diff --git a/tests/integration/test_record_mode.py b/tests/integration/test_record_mode.py index 881ccf5..2b85eef 100644 --- a/tests/integration/test_record_mode.py +++ b/tests/integration/test_record_mode.py @@ -124,9 +124,11 @@ def test_none_record_mode(tmpdir, httpbin): # Cassette file doesn't exist, yet we are trying to make a request. # raise hell. testfile = str(tmpdir.join("recordmode.yml")) - with vcr.use_cassette(testfile, record_mode=vcr.mode.NONE): - with pytest.raises(CannotOverwriteExistingCassetteException): - urlopen(httpbin.url).read() + with ( + vcr.use_cassette(testfile, record_mode=vcr.mode.NONE), + pytest.raises(CannotOverwriteExistingCassetteException), + ): + urlopen(httpbin.url).read() def test_none_record_mode_with_existing_cassette(tmpdir, httpbin): diff --git a/tests/integration/test_register_persister.py b/tests/integration/test_register_persister.py index 375f14b..8d3ef15 100644 --- a/tests/integration/test_register_persister.py +++ b/tests/integration/test_register_persister.py @@ -83,6 +83,5 @@ def test_load_cassette_persister_exception_handling(tmpdir, httpbin): with my_vcr.use_cassette("bad/encoding") as cass: assert len(cass) == 0 - with pytest.raises(ValueError): - with my_vcr.use_cassette("bad/buggy") as cass: - pass + with pytest.raises(ValueError), my_vcr.use_cassette("bad/buggy") as cass: + pass diff --git a/tests/integration/test_stubs.py b/tests/integration/test_stubs.py index 37a1b47..942c931 100644 --- a/tests/integration/test_stubs.py +++ b/tests/integration/test_stubs.py @@ -66,7 +66,7 @@ def test_original_decoded_response_is_not_modified(tmpdir, httpbin): # Assert that we do not modify the original response while appending # to the cassette. - assert "gzip" == inside.headers["content-encoding"] + assert inside.headers["content-encoding"] == "gzip" # They should effectively be the same response. inside_headers = (h for h in inside.headers.items() if h[0].lower() != "date") @@ -122,7 +122,7 @@ def test_original_response_is_not_modified_by_before_filter(tmpdir, httpbin): # Furthermore, the responses should be identical. inside_body = json.loads(inside.read()) outside_body = json.loads(outside.read()) - assert not inside_body[field_to_scrub] == replacement + assert inside_body[field_to_scrub] != replacement assert inside_body[field_to_scrub] == outside_body[field_to_scrub] # Ensure that when a cassette exists, the scrubbed response is returned. diff --git a/tests/integration/test_tornado.py b/tests/integration/test_tornado.py index 205825a..d20ca33 100644 --- a/tests/integration/test_tornado.py +++ b/tests/integration/test_tornado.py @@ -42,8 +42,13 @@ def scheme(request): return request.param -@pytest.fixture(params=["curl", "default"]) +@pytest.fixture(params=["simple", "curl", "default"]) def get_client(request): + if request.param == "simple": + from tornado import simple_httpclient as simple + + return lambda: simple.SimpleAsyncHTTPClient() + if request.param == "curl": curl = pytest.importorskip("tornado.curl_httpclient") return lambda: curl.CurlAsyncHTTPClient() @@ -75,7 +80,7 @@ def test_status_code(get_client, scheme, tmpdir): with vcr.use_cassette(str(tmpdir.join("atts.yaml"))) as cass: assert status_code == (yield get(get_client(), url)).code - assert 1 == cass.play_count + assert cass.play_count == 1 @pytest.mark.online @@ -88,7 +93,7 @@ def test_headers(get_client, scheme, tmpdir): with vcr.use_cassette(str(tmpdir.join("headers.yaml"))) as cass: assert headers == (yield get(get_client(), url)).headers - assert 1 == cass.play_count + assert cass.play_count == 1 @pytest.mark.online @@ -102,7 +107,7 @@ def test_body(get_client, tmpdir, scheme): with vcr.use_cassette(str(tmpdir.join("body.yaml"))) as cass: assert content == (yield get(get_client(), url)).body - assert 1 == cass.play_count + assert cass.play_count == 1 @gen_test @@ -115,7 +120,7 @@ def test_effective_url(get_client, tmpdir, httpbin): with vcr.use_cassette(str(tmpdir.join("url.yaml"))) as cass: assert effective_url == (yield get(get_client(), url)).effective_url - assert 1 == cass.play_count + assert cass.play_count == 1 @pytest.mark.online @@ -131,7 +136,7 @@ def test_auth(get_client, tmpdir, scheme): two = yield get(get_client(), url, auth_username=auth[0], auth_password=auth[1]) assert one.body == two.body assert one.code == two.code - assert 1 == cass.play_count + assert cass.play_count == 1 @pytest.mark.online @@ -155,7 +160,7 @@ def test_auth_failed(get_client, tmpdir, scheme): assert exc_info.value.code == 401 assert one.body == two.body assert one.code == two.code == 401 - assert 1 == cass.play_count + assert cass.play_count == 1 @pytest.mark.online @@ -171,7 +176,7 @@ def test_post(get_client, tmpdir, scheme): req2 = (yield post(get_client(), url, data)).body assert req1 == req2 - assert 1 == cass.play_count + assert cass.play_count == 1 @gen_test @@ -229,7 +234,7 @@ def test_gzip(get_client, tmpdir, scheme): with vcr.use_cassette(str(tmpdir.join("gzip.yaml"))) as cass: response = yield get(get_client(), url, **kwargs) assert_is_json_bytes(response.body) - assert 1 == cass.play_count + assert cass.play_count == 1 @pytest.mark.online @@ -242,7 +247,7 @@ def test_https_with_cert_validation_disabled(get_client, tmpdir): with vcr.use_cassette(cass_path) as cass: yield get(get_client(), "https://httpbin.org", validate_cert=False) - assert 1 == cass.play_count + assert cass.play_count == 1 @gen_test diff --git a/tests/integration/test_wild.py b/tests/integration/test_wild.py index 2db7744..421570b 100644 --- a/tests/integration/test_wild.py +++ b/tests/integration/test_wild.py @@ -62,13 +62,12 @@ def test_flickr_should_respond_with_200(tmpdir): def test_cookies(tmpdir, httpbin): testfile = str(tmpdir.join("cookies.yml")) - with vcr.use_cassette(testfile): - with requests.Session() as s: - s.get(httpbin.url + "/cookies/set?k1=v1&k2=v2") - assert s.cookies.keys() == ["k1", "k2"] + with vcr.use_cassette(testfile), requests.Session() as s: + s.get(httpbin.url + "/cookies/set?k1=v1&k2=v2") + assert s.cookies.keys() == ["k1", "k2"] - r2 = s.get(httpbin.url + "/cookies") - assert sorted(r2.json()["cookies"].keys()) == ["k1", "k2"] + r2 = s.get(httpbin.url + "/cookies") + assert sorted(r2.json()["cookies"].keys()) == ["k1", "k2"] @pytest.mark.online diff --git a/tests/unit/test_cassettes.py b/tests/unit/test_cassettes.py index f2431e4..0397d1f 100644 --- a/tests/unit/test_cassettes.py +++ b/tests/unit/test_cassettes.py @@ -227,9 +227,11 @@ def test_nesting_cassette_context_managers(*args): assert_get_response_body_is("first_response") # 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") + with ( + Cassette.use(path="test") as second_cassette, + mock.patch.object(second_cassette, "play_response", return_value=second_response), + ): + assert_get_response_body_is("second_response") # Now the first cassette should be back in effect assert_get_response_body_is("first_response") diff --git a/tests/unit/test_serialize.py b/tests/unit/test_serialize.py index 270cd12..6e60fef 100644 --- a/tests/unit/test_serialize.py +++ b/tests/unit/test_serialize.py @@ -8,15 +8,13 @@ from vcr.serializers import compat, jsonserializer, yamlserializer def test_deserialize_old_yaml_cassette(): - with open("tests/fixtures/migration/old_cassette.yaml") as f: - with pytest.raises(ValueError): - deserialize(f.read(), yamlserializer) + with open("tests/fixtures/migration/old_cassette.yaml") as f, pytest.raises(ValueError): + deserialize(f.read(), yamlserializer) def test_deserialize_old_json_cassette(): - with open("tests/fixtures/migration/old_cassette.json") as f: - with pytest.raises(ValueError): - deserialize(f.read(), jsonserializer) + with open("tests/fixtures/migration/old_cassette.json") as f, pytest.raises(ValueError): + deserialize(f.read(), jsonserializer) def test_deserialize_new_yaml_cassette(): diff --git a/vcr/request.py b/vcr/request.py index bc569f4..a4011b0 100644 --- a/vcr/request.py +++ b/vcr/request.py @@ -1,5 +1,6 @@ import logging import warnings +from contextlib import suppress from io import BytesIO from urllib.parse import parse_qsl, urlparse @@ -80,10 +81,9 @@ class Request: def port(self): port = self.parsed_uri.port if port is None: - try: + with suppress(KeyError): port = {"https": 443, "http": 80}[self.parsed_uri.scheme] - except KeyError: - pass + return port @property diff --git a/vcr/stubs/__init__.py b/vcr/stubs/__init__.py index 44a4432..2c3b8b5 100644 --- a/vcr/stubs/__init__.py +++ b/vcr/stubs/__init__.py @@ -1,6 +1,7 @@ """Stubs for patching HTTP and HTTPS requests""" import logging +from contextlib import suppress from http.client import HTTPConnection, HTTPResponse, HTTPSConnection from io import BytesIO @@ -77,7 +78,7 @@ class VCRHTTPResponse(HTTPResponse): # libraries trying to process a chunked response. By removing the # transfer-encoding: chunked header, this should cause the downstream # libraries to process this as a non-chunked response. - te_key = [h for h in headers.keys() if h.upper() == "TRANSFER-ENCODING"] + te_key = [h for h in headers if h.upper() == "TRANSFER-ENCODING"] if te_key: del headers[te_key[0]] self.headers = self.msg = parse_headers(headers) @@ -370,12 +371,8 @@ class VCRConnection: TODO: Separately setting the attribute on the two instances is not ideal. We should switch to a proxying implementation. """ - try: + with suppress(AttributeError): setattr(self.real_connection, name, value) - except AttributeError: - # raised if real_connection has not been set yet, such as when - # we're setting the real_connection itself for the first time - pass super().__setattr__(name, value) diff --git a/vcr/stubs/tornado_stubs.py b/vcr/stubs/tornado_stubs.py index aee2890..060897c 100644 --- a/vcr/stubs/tornado_stubs.py +++ b/vcr/stubs/tornado_stubs.py @@ -74,7 +74,7 @@ def vcr_fetch_impl(cassette, real_fetch_impl): return callback(response) def new_callback(response): - headers = [(k, response.headers.get_list(k)) for k in response.headers.keys()] + headers = [(k, response.headers.get_list(k)) for k in response.headers] vcr_response = { "status": {"code": response.code, "message": response.reason},