Use match parameter in pytest.raises()
MODIFY
test/contrib/test_socks.py
test/contrib/test_socks.py
@@ -219,9 +219,8 @@ def handle_socks4_negotiation(sock, username=None):
class TestSOCKSProxyManager:
def test_invalid_socks_version_is_valueerror(self):
- with pytest.raises(ValueError) as e:
+ with pytest.raises(ValueError, match="Unable to determine SOCKS version"):
socks.SOCKSProxyManager(proxy_url="http://example.org")
- assert "Unable to determine SOCKS version" in e.value.args[0]
class TestSocks5Proxy(IPV4SocketDummyServerTestCase):
@@ -470,9 +469,10 @@ class TestSocks5Proxy(IPV4SocketDummyServerTestCase):
with socks.SOCKSProxyManager(
proxy_url, username="user", password="badpass"
) as pm:
- with pytest.raises(NewConnectionError) as e:
+ with pytest.raises(
+ NewConnectionError, match="SOCKS5 authentication failed"
+ ):
pm.request("GET", "http://example.com", retries=False)
- assert "SOCKS5 authentication failed" in str(e.value)
def test_source_address_works(self):
expected_port = _get_free_port(self.host)
@@ -683,9 +683,8 @@ class TestSOCKS4Proxy(IPV4SocketDummyServerTestCase):
self._start_server(request_handler)
proxy_url = f"socks4a://{self.host}:{self.port}"
with socks.SOCKSProxyManager(proxy_url, username="baduser") as pm:
- with pytest.raises(NewConnectionError) as e:
+ with pytest.raises(NewConnectionError, match="different user-ids"):
pm.request("GET", "http://example.com", retries=False)
- assert "different user-ids" in str(e.value)
class TestSOCKSWithTLS(IPV4SocketDummyServerTestCase):

MODIFY
test/test_collections.py
test/test_collections.py
@@ -249,9 +249,10 @@ class TestHTTPHeaderDict:
@pytest.mark.parametrize("args", [(1, 2), (1, 2, 3, 4, 5)])
def test_extend_with_wrong_number_of_args_is_typeerror(self, d, args):
- with pytest.raises(TypeError) as err:
+ with pytest.raises(
+ TypeError, match=r"extend\(\) takes at most 1 positional arguments"
+ ):
d.extend(*args)
- assert "extend() takes at most 1 positional arguments" in err.value.args[0]
def test_copy(self, d):
h = d.copy()

MODIFY
test/test_response.py
test/test_response.py
import contextlib
import http.client as httplib
-import re
import socket
import ssl
import zlib
@@ -357,9 +356,8 @@ class TestResponse:
# https://github.com/urllib3/urllib3/issues/1305
fp = BytesIO(b"hello\nworld")
resp = HTTPResponse(fp, preload_content=False)
- with pytest.raises(ValueError) as ctx:
+ with pytest.raises(ValueError, match="readline of closed file"):
list(BufferedReader(resp))
- assert str(ctx.value) == "readline of closed file"
b = b"fooandahalf"
fp = BytesIO(b)
@@ -388,9 +386,8 @@ class TestResponse:
reader.close()
assert reader.closed
assert resp.closed
- with pytest.raises(ValueError) as ctx:
+ with pytest.raises(ValueError, match="readline of closed file"):
next(reader)
- assert str(ctx.value) == "readline of closed file"
def test_io_textiowrapper(self):
fp = BytesIO(b"\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f")
@@ -408,9 +405,8 @@ class TestResponse:
b"\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f\n\xce\xb1\xce\xb2\xce\xb3\xce\xb4"
)
resp = HTTPResponse(fp, preload_content=False)
- with pytest.raises(ValueError) as ctx:
+ with pytest.raises(ValueError, match="I/O operation on closed file.?"):
list(TextIOWrapper(resp))
- assert re.match("I/O operation on closed file.?", str(ctx.value))
def test_io_not_autoclose_textiowrapper(self):
fp = BytesIO(
@@ -428,9 +424,8 @@ class TestResponse:
reader.close()
assert reader.closed
assert resp.closed
- with pytest.raises(ValueError) as ctx:
+ with pytest.raises(ValueError, match="I/O operation on closed file.?"):
next(reader)
- assert re.match("I/O operation on closed file.?", str(ctx.value))
def test_streaming(self):
fp = BytesIO(b"foo")

MODIFY
test/test_retry.py
test/test_retry.py
@@ -123,11 +123,9 @@ class TestRetry:
retry = Retry(status=2)
retry = retry.increment(response=resp)
retry = retry.increment(response=resp)
- with pytest.raises(MaxRetryError) as e:
+ msg = ResponseError.SPECIFIC_ERROR.format(status_code=400)
+ with pytest.raises(MaxRetryError, match=msg):
retry.increment(response=resp)
- assert str(e.value.reason) == ResponseError.SPECIFIC_ERROR.format(
- status_code=400
- )
def test_backoff(self):
""" Backoff is computed correctly """
@@ -218,36 +216,32 @@ class TestRetry:
def test_error_message(self):
retry = Retry(total=0)
- with pytest.raises(MaxRetryError) as e:
+ with pytest.raises(MaxRetryError, match="None: read timed out") as e:
retry = retry.increment(
method="GET", error=ReadTimeoutError(None, "/", "read timed out")
)
assert "Caused by redirect" not in str(e.value)
- assert str(e.value.reason) == "None: read timed out"
retry = Retry(total=1)
- with pytest.raises(MaxRetryError) as e:
retry = retry.increment("POST", "/")
+ with pytest.raises(MaxRetryError, match=ResponseError.GENERIC_ERROR) as e:
retry = retry.increment("POST", "/")
assert "Caused by redirect" not in str(e.value)
assert isinstance(e.value.reason, ResponseError)
- assert str(e.value.reason) == ResponseError.GENERIC_ERROR
retry = Retry(total=1)
response = HTTPResponse(status=500)
- with pytest.raises(MaxRetryError) as e:
+ msg = ResponseError.SPECIFIC_ERROR.format(status_code=500)
retry = retry.increment("POST", "/", response=response)
+ with pytest.raises(MaxRetryError, match=msg) as e:
retry = retry.increment("POST", "/", response=response)
assert "Caused by redirect" not in str(e.value)
- msg = ResponseError.SPECIFIC_ERROR.format(status_code=500)
- assert str(e.value.reason) == msg
retry = Retry(connect=1)
- with pytest.raises(MaxRetryError) as e:
retry = retry.increment(error=ConnectTimeoutError("conntimeout"))
+ with pytest.raises(MaxRetryError, match="conntimeout") as e:
retry = retry.increment(error=ConnectTimeoutError("conntimeout"))
assert "Caused by redirect" not in str(e.value)
- assert str(e.value.reason) == "conntimeout"
def test_history(self):
retry = Retry(total=10, allowed_methods=frozenset(["GET", "POST"]))

MODIFY
test/test_util.py
test/test_util.py
@@ -564,9 +564,8 @@ class TestUtil:
],
)
def test_invalid_timeouts(self, kwargs, message):
- with pytest.raises(ValueError) as e:
+ with pytest.raises(ValueError, match=message):
Timeout(**kwargs)
- assert message in str(e.value)
@patch("urllib3.util.timeout.current_time")
def test_timeout(self, current_time):
@@ -737,9 +736,11 @@ class TestUtil:
@pytest.mark.parametrize("host", [".localhost", "...", "t" * 64])
def test_create_connection_with_invalid_idna_labels(self, host):
- with pytest.raises(LocationParseError) as ctx:
+ with pytest.raises(
+ LocationParseError,
+ match=f"Failed to parse: '{host}', label empty or too long",
+ ):
create_connection((host, 80))
- assert str(ctx.value) == f"Failed to parse: '{host}', label empty or too long"
@pytest.mark.parametrize(
"host",

MODIFY
test/with_dummyserver/test_connectionpool.py
test/with_dummyserver/test_connectionpool.py
@@ -921,15 +921,13 @@ class TestConnectionPool(HTTPDummyServerTestCase):
@pytest.mark.parametrize("chunked", [True, False])
def test_skip_header_non_supported(self, header, chunked):
with HTTPConnectionPool(self.host, self.port) as pool:
- with pytest.raises(ValueError) as e:
+ with pytest.raises(
+ ValueError,
+ match="urllib3.util.SKIP_HEADER only supports 'Accept-Encoding', 'Host', 'User-Agent'",
+ ) as e:
pool.request(
"GET", "/headers", headers={header: SKIP_HEADER}, chunked=chunked
)
- assert (
- str(e.value)
- == "urllib3.util.SKIP_HEADER only supports 'Accept-Encoding', 'Host', 'User-Agent'"
- )
-
# Ensure that the error message stays up to date with 'SKIP_HEADER_SUPPORTED_HEADERS'
assert all(
("'" + header.title() + "'") in str(e.value)
@@ -1303,9 +1301,10 @@ class TestFileBodiesOnRetryOrRedirect(HTTPDummyServerTestCase):
# which is unsupported by BytesIO.
headers = {"Content-Length": "8"}
with HTTPConnectionPool(self.host, self.port, timeout=0.1) as pool:
- with pytest.raises(UnrewindableBodyError) as e:
+ with pytest.raises(
+ UnrewindableBodyError, match="Unable to record file position for"
+ ):
pool.urlopen("PUT", url, headers=headers, body=body)
- assert "Unable to record file position for" in str(e.value)
class TestRetryPoolSize(HTTPDummyServerTestCase):

MODIFY
test/with_dummyserver/test_https.py
test/with_dummyserver/test_https.py
@@ -192,10 +192,9 @@ class TestHTTPS(HTTPSDummyServerTestCase):
cert_file=os.path.join(self.certs_dir, CLIENT_CERT),
key_password=None,
) as https_pool:
- with pytest.raises(MaxRetryError) as e:
+ with pytest.raises(MaxRetryError, match="password is required") as e:
https_pool.request("GET", "/certificate")
- assert "password is required" in str(e.value)
assert isinstance(e.value.reason, SSLError)
def test_verified(self):

MODIFY
test/with_dummyserver/test_proxy_poolmanager.py
test/with_dummyserver/test_proxy_poolmanager.py
@@ -88,22 +88,22 @@ class TestHTTPProxyManager(HTTPDummyProxyTestCase):
r = https.request("GET", f"{self.http_url}/")
assert r.status == 200
- with pytest.raises(ProxySchemeUnsupported) as excinfo:
+ with pytest.raises(
+ ProxySchemeUnsupported, match="isn't available on non-native SSLContext"
+ ):
https.request("GET", f"{self.https_url}/")
- assert "isn't available on non-native SSLContext" in str(excinfo.value)
-
@onlySecureTransport
def test_https_proxy_securetransport_not_supported(self):
with proxy_from_url(self.https_proxy_url, ca_certs=DEFAULT_CA) as https:
r = https.request("GET", f"{self.http_url}/")
assert r.status == 200
- with pytest.raises(ProxySchemeUnsupported) as excinfo:
+ with pytest.raises(
+ ProxySchemeUnsupported, match="isn't available on non-native SSLContext"
+ ):
https.request("GET", f"{self.https_url}/")
- assert "isn't available on non-native SSLContext" in str(excinfo.value)
-
def test_https_proxy_forwarding_for_https(self):
with proxy_from_url(
self.https_proxy_url,
@@ -183,10 +183,9 @@ class TestHTTPProxyManager(HTTPDummyProxyTestCase):
)
https_fail_pool = http._new_pool("https", "127.0.0.1", self.https_port)
- with pytest.raises(MaxRetryError) as e:
+ with pytest.raises(MaxRetryError, match="doesn't match") as e:
https_fail_pool.request("GET", "/", retries=0)
assert isinstance(e.value.reason, SSLError)
- assert "doesn't match" in str(e.value.reason)
def test_redirect(self):
with proxy_from_url(self.proxy_url) as http:

Use match parameter in pytest.raises()

Co-authored-by: Seth Michael Larson <sethmichaellarson@gmail.com>