From 31db28210facc24b96afecbc3b41588d0dc6fded Mon Sep 17 00:00:00 2001 From: lmvlmv <lverrall@gmail.com> Date: Tue, 29 Oct 2019 12:19:10 +0000 Subject: [PATCH 01/19] Preserve chunked argument in urlopen() retry (#1715) --- dummyserver/testcase.py | 9 +++++-- src/urllib3/connectionpool.py | 1 + .../with_dummyserver/test_chunked_transfer.py | 24 ++++++++++++++++++- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/dummyserver/testcase.py b/dummyserver/testcase.py index bf83b6b4..07337d9a 100644 --- a/dummyserver/testcase.py +++ b/dummyserver/testcase.py @@ -15,8 +15,13 @@ def consume_socket(sock, chunks=65536): - while not sock.recv(chunks).endswith(b"\r\n\r\n"): - pass + consumed = bytearray() + while True: + b = sock.recv(chunks) + consumed += b + if b.endswith(b"\r\n\r\n"): + break + return consumed class SocketDummyServerTestCase(object): diff --git a/src/urllib3/connectionpool.py b/src/urllib3/connectionpool.py index 58ce1cf8..26c4bd70 100644 --- a/src/urllib3/connectionpool.py +++ b/src/urllib3/connectionpool.py @@ -839,6 +839,7 @@ def drain_and_release_conn(response): timeout=timeout, pool_timeout=pool_timeout, release_conn=release_conn, + chunked=chunked, body_pos=body_pos, **response_kw ) diff --git a/test/with_dummyserver/test_chunked_transfer.py b/test/with_dummyserver/test_chunked_transfer.py index a7512cef..a0807d5b 100644 --- a/test/with_dummyserver/test_chunked_transfer.py +++ b/test/with_dummyserver/test_chunked_transfer.py @@ -1,7 +1,8 @@ # -*- coding: utf-8 -*- from urllib3 import HTTPConnectionPool -from dummyserver.testcase import SocketDummyServerTestCase +from urllib3.util.retry import Retry +from dummyserver.testcase import SocketDummyServerTestCase, consume_socket class TestChunkedTransfer(SocketDummyServerTestCase): @@ -100,3 +101,24 @@ def test_provides_default_host_header(self): host_headers = [x for x in header_lines if x.startswith(b"host")] assert len(host_headers) == 1 + + def test_preserve_chunked_on_retry(self): + self.chunked_requests = 0 + + def socket_handler(listener): + for _ in range(2): + sock = listener.accept()[0] + request = consume_socket(sock) + if b"Transfer-Encoding: chunked" in request.split(b"\r\n"): + self.chunked_requests += 1 + + sock.send(b"HTTP/1.1 404 Not Found\r\n\r\n") + sock.close() + + self._start_server(socket_handler) + with HTTPConnectionPool(self.host, self.port) as pool: + retries = Retry(total=1, raise_on_status=False, status_forcelist=[404]) + pool.urlopen( + "GET", "/", chunked=True, preload_content=False, retries=retries + ) + assert self.chunked_requests == 2 From 15e64de5a67a87f5350538cff1f011cb03ffde03 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 29 Oct 2019 16:22:27 +0400 Subject: [PATCH 02/19] Retry failed dummyserver tests once (#1718) --- dev-requirements.txt | 1 + noxfile.py | 1 + test/with_dummyserver/test_chunked_transfer.py | 5 +++++ test/with_dummyserver/test_connectionpool.py | 2 ++ test/with_dummyserver/test_https.py | 2 ++ test/with_dummyserver/test_no_ssl.py | 4 ++++ test/with_dummyserver/test_poolmanager.py | 3 +++ test/with_dummyserver/test_proxy_poolmanager.py | 3 +++ test/with_dummyserver/test_socketlevel.py | 3 +++ 9 files changed, 24 insertions(+) diff --git a/dev-requirements.txt b/dev-requirements.txt index 4e0dd10d..644aa48f 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -6,6 +6,7 @@ PySocks==1.6.8 pkginfo==1.4.2 pytest-timeout==1.3.3 pytest==4.6.4 +flaky==3.6.1 # https://github.com/ionelmc/python-lazy-object-proxy/issues/30 lazy-object-proxy==1.4.0 diff --git a/noxfile.py b/noxfile.py index 66d664f5..33090226 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,6 +30,7 @@ def tests_impl(session, extras="socks,secure,brotli"): "pytest", "-r", "sx", + "--no-success-flaky-report", *(session.posargs or ("test/",)), env={"PYTHONWARNINGS": "always::DeprecationWarning"} ) diff --git a/test/with_dummyserver/test_chunked_transfer.py b/test/with_dummyserver/test_chunked_transfer.py index a0807d5b..c62a92c4 100644 --- a/test/with_dummyserver/test_chunked_transfer.py +++ b/test/with_dummyserver/test_chunked_transfer.py @@ -1,9 +1,14 @@ # -*- coding: utf-8 -*- +import pytest + from urllib3 import HTTPConnectionPool from urllib3.util.retry import Retry from dummyserver.testcase import SocketDummyServerTestCase, consume_socket +# Retry failed tests +pytestmark = pytest.mark.flaky + class TestChunkedTransfer(SocketDummyServerTestCase): def start_chunked_handler(self): diff --git a/test/with_dummyserver/test_connectionpool.py b/test/with_dummyserver/test_connectionpool.py index e899ba9a..d8193d20 100644 --- a/test/with_dummyserver/test_connectionpool.py +++ b/test/with_dummyserver/test_connectionpool.py @@ -30,6 +30,8 @@ from threading import Event +pytestmark = pytest.mark.flaky + log = logging.getLogger("urllib3.connectionpool") log.setLevel(logging.NOTSET) log.addHandler(logging.StreamHandler(sys.stdout)) diff --git a/test/with_dummyserver/test_https.py b/test/with_dummyserver/test_https.py index 3621d5ba..7c5b6e27 100644 --- a/test/with_dummyserver/test_https.py +++ b/test/with_dummyserver/test_https.py @@ -55,6 +55,8 @@ from urllib3.util.timeout import Timeout import urllib3.util as util +# Retry failed tests +pytestmark = pytest.mark.flaky ResourceWarning = getattr( six.moves.builtins, "ResourceWarning", type("ResourceWarning", (), {}) diff --git a/test/with_dummyserver/test_no_ssl.py b/test/with_dummyserver/test_no_ssl.py index f2d8059b..7f4d350a 100644 --- a/test/with_dummyserver/test_no_ssl.py +++ b/test/with_dummyserver/test_no_ssl.py @@ -3,12 +3,16 @@ Note: Import urllib3 inside the test functions to get the importblocker to work """ +import pytest from ..test_no_ssl import TestWithoutSSL from dummyserver.testcase import HTTPDummyServerTestCase, HTTPSDummyServerTestCase import urllib3 +# Retry failed tests +pytestmark = pytest.mark.flaky + class TestHTTPWithoutSSL(HTTPDummyServerTestCase, TestWithoutSSL): def test_simple(self): diff --git a/test/with_dummyserver/test_poolmanager.py b/test/with_dummyserver/test_poolmanager.py index 947c3658..a470113b 100644 --- a/test/with_dummyserver/test_poolmanager.py +++ b/test/with_dummyserver/test_poolmanager.py @@ -9,6 +9,9 @@ from urllib3.exceptions import MaxRetryError from urllib3.util.retry import Retry +# Retry failed tests +pytestmark = pytest.mark.flaky + class TestPoolManager(HTTPDummyServerTestCase): @classmethod diff --git a/test/with_dummyserver/test_proxy_poolmanager.py b/test/with_dummyserver/test_proxy_poolmanager.py index aef79335..d16a38a9 100644 --- a/test/with_dummyserver/test_proxy_poolmanager.py +++ b/test/with_dummyserver/test_proxy_poolmanager.py @@ -12,6 +12,9 @@ from urllib3.exceptions import MaxRetryError, SSLError, ProxyError, ConnectTimeoutError from urllib3.connectionpool import connection_from_url, VerifiedHTTPSConnection +# Retry failed tests +pytestmark = pytest.mark.flaky + class TestHTTPProxyManager(HTTPDummyProxyTestCase): @classmethod diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py index 47d114f9..359e8755 100644 --- a/test/with_dummyserver/test_socketlevel.py +++ b/test/with_dummyserver/test_socketlevel.py @@ -49,6 +49,9 @@ class MimeToolMessage(object): from test import fails_on_travis_gce, requires_ssl_context_keyfile_password +# Retry failed tests +pytestmark = pytest.mark.flaky + class TestCookies(SocketDummyServerTestCase): def test_multi_setcookie(self): From 9fca285a9b674034e8bb4e242a6942cecf8ec732 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 29 Oct 2019 16:22:38 +0400 Subject: [PATCH 03/19] Remove Python 3.1 workaround (#1716) --- test/with_dummyserver/test_chunked_transfer.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/test/with_dummyserver/test_chunked_transfer.py b/test/with_dummyserver/test_chunked_transfer.py index c62a92c4..d9a87b20 100644 --- a/test/with_dummyserver/test_chunked_transfer.py +++ b/test/with_dummyserver/test_chunked_transfer.py @@ -67,12 +67,7 @@ def test_bytestring_body(self): self._test_body(b"thisshouldbeonechunk\r\nasdf") def test_unicode_body(self): - # Define u'thisshouldbeonechunk\r\näöüß' in a way, so that python3.1 - # does not suffer a syntax error - chunk = b"thisshouldbeonechunk\r\n\xc3\xa4\xc3\xb6\xc3\xbc\xc3\x9f".decode( - "utf-8" - ) - self._test_body(chunk) + self._test_body(u"thisshouldbeonechunk\r\näöüß") def test_empty_body(self): self._test_body(None) From 42a3ef3ce763ec8434a6ae48bac59434e1b0c9b1 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 29 Oct 2019 16:23:22 +0400 Subject: [PATCH 04/19] Prefer pytest.raises over Testcase.fail (#1712) It reuses a standard mechanism, is less verbose and avoids including code that is never executed. --- test/contrib/test_socks.py | 14 ++--- test/with_dummyserver/test_connectionpool.py | 32 ++++------ test/with_dummyserver/test_https.py | 59 ++++++++----------- test/with_dummyserver/test_poolmanager.py | 40 +++---------- .../test_proxy_poolmanager.py | 50 +++++----------- test/with_dummyserver/test_socketlevel.py | 13 +--- 6 files changed, 65 insertions(+), 143 deletions(-) diff --git a/test/contrib/test_socks.py b/test/contrib/test_socks.py index cc0a69b3..bf1bd090 100644 --- a/test/contrib/test_socks.py +++ b/test/contrib/test_socks.py @@ -440,12 +440,9 @@ def request_handler(listener): with socks.SOCKSProxyManager( proxy_url, username="user", password="badpass" ) as pm: - try: + with pytest.raises(NewConnectionError) as e: pm.request("GET", "http://example.com", retries=False) - except NewConnectionError as e: - assert "SOCKS5 authentication failed" in str(e) - else: - self.fail("Did not raise") + assert "SOCKS5 authentication failed" in str(e.value) def test_source_address_works(self): expected_port = _get_free_port(self.host) @@ -655,12 +652,9 @@ def request_handler(listener): self._start_server(request_handler) proxy_url = "socks4a://%s:%s" % (self.host, self.port) with socks.SOCKSProxyManager(proxy_url, username="baduser") as pm: - try: + with pytest.raises(NewConnectionError) as e: pm.request("GET", "http://example.com", retries=False) - except NewConnectionError as e: - assert "different user-ids" in str(e) - else: - self.fail("Did not raise") + assert "different user-ids" in str(e.value) class TestSOCKSWithTLS(IPV4SocketDummyServerTestCase): diff --git a/test/with_dummyserver/test_connectionpool.py b/test/with_dummyserver/test_connectionpool.py index d8193d20..bd7fec1a 100644 --- a/test/with_dummyserver/test_connectionpool.py +++ b/test/with_dummyserver/test_connectionpool.py @@ -75,9 +75,8 @@ def test_conn_closed(self): conn = pool._get_conn() pool._put_conn(conn) try: - pool.urlopen("GET", "/") - self.fail("The request should fail with a timeout error.") - except ReadTimeoutError: + with pytest.raises(ReadTimeoutError): + pool.urlopen("GET", "/") if conn.sock: with pytest.raises(socket.error): conn.sock.recv(1024) @@ -385,11 +384,9 @@ def test_connection_error_retries(self): """ ECONNREFUSED error should raise a connection error, with retries """ port = find_unused_port() with HTTPConnectionPool(self.host, port) as pool: - try: + with pytest.raises(MaxRetryError) as e: pool.request("GET", "/", retries=Retry(connect=3)) - self.fail("Should have failed with a connection error.") - except MaxRetryError as e: - assert type(e.reason) == NewConnectionError + assert type(e.value.reason) == NewConnectionError def test_timeout_success(self): timeout = Timeout(connect=3, read=5, total=None) @@ -441,11 +438,9 @@ def test_redirect(self): def test_bad_connect(self): with HTTPConnectionPool("badhost.invalid", self.port) as pool: - try: + with pytest.raises(MaxRetryError) as e: pool.request("GET", "/", retries=5) - self.fail("should raise timeout exception here") - except MaxRetryError as e: - assert type(e.reason) == NewConnectionError + assert type(e.value.reason) == NewConnectionError def test_keepalive(self): with HTTPConnectionPool(self.host, self.port, block=True, maxsize=1) as pool: @@ -823,13 +818,8 @@ def test_mixed_case_hostname(self): class TestRetry(HTTPDummyServerTestCase): def test_max_retry(self): with HTTPConnectionPool(self.host, self.port) as pool: - try: - r = pool.request("GET", "/redirect", fields={"target": "/"}, retries=0) - self.fail( - "Failed to raise MaxRetryError exception, returned %r" % r.status - ) - except MaxRetryError: - pass + with pytest.raises(MaxRetryError): + pool.request("GET", "/redirect", fields={"target": "/"}, retries=0) def test_disabled_retry(self): """ Disabled retries should disable redirect handling. """ @@ -1133,11 +1123,9 @@ def tell(self): # which is unsupported by BytesIO. headers = {"Content-Length": "8"} with HTTPConnectionPool(self.host, self.port, timeout=0.1) as pool: - try: + with pytest.raises(UnrewindableBodyError) as e: pool.urlopen("PUT", url, headers=headers, body=body) - self.fail("PUT successful despite failed rewind.") - except UnrewindableBodyError as e: - assert "Unable to record file position for" in str(e) + assert "Unable to record file position for" in str(e.value) class TestRetryPoolSize(HTTPDummyServerTestCase): diff --git a/test/with_dummyserver/test_https.py b/test/with_dummyserver/test_https.py index 7c5b6e27..9c3077f8 100644 --- a/test/with_dummyserver/test_https.py +++ b/test/with_dummyserver/test_https.py @@ -291,54 +291,45 @@ def test_invalid_common_name(self): with HTTPSConnectionPool( "127.0.0.1", self.port, cert_reqs="CERT_REQUIRED", ca_certs=DEFAULT_CA ) as https_pool: - try: + with pytest.raises(MaxRetryError) as e: https_pool.request("GET", "/") - self.fail("Didn't raise SSL invalid common name") - except MaxRetryError as e: - assert isinstance(e.reason, SSLError) - assert "doesn't match" in str( - e.reason - ) or "certificate verify failed" in str(e.reason) + assert isinstance(e.value.reason, SSLError) + assert "doesn't match" in str( + e.value.reason + ) or "certificate verify failed" in str(e.value.reason) def test_verified_with_bad_ca_certs(self): with HTTPSConnectionPool( self.host, self.port, cert_reqs="CERT_REQUIRED", ca_certs=DEFAULT_CA_BAD ) as https_pool: - try: + with pytest.raises(MaxRetryError) as e: https_pool.request("GET", "/") - self.fail("Didn't raise SSL error with bad CA certs") - except MaxRetryError as e: - assert isinstance(e.reason, SSLError) - assert "certificate verify failed" in str(e.reason), ( - "Expected 'certificate verify failed', instead got: %r" % e.reason - ) + assert isinstance(e.value.reason, SSLError) + assert "certificate verify failed" in str(e.value.reason), ( + "Expected 'certificate verify failed', instead got: %r" % e.value.reason + ) def test_verified_without_ca_certs(self): # default is cert_reqs=None which is ssl.CERT_NONE with HTTPSConnectionPool( self.host, self.port, cert_reqs="CERT_REQUIRED" ) as https_pool: - try: + with pytest.raises(MaxRetryError) as e: https_pool.request("GET", "/") - self.fail( - "Didn't raise SSL error with no CA certs when" - "CERT_REQUIRED is set" - ) - except MaxRetryError as e: - assert isinstance(e.reason, SSLError) - # there is a different error message depending on whether or - # not pyopenssl is injected - assert ( - "No root certificates specified" in str(e.reason) - # PyPy sometimes uses all-caps here - or "certificate verify failed" in str(e.reason).lower() - or "invalid certificate chain" in str(e.reason) - ), ( - "Expected 'No root certificates specified', " - "'certificate verify failed', or " - "'invalid certificate chain', " - "instead got: %r" % e.reason - ) + assert isinstance(e.value.reason, SSLError) + # there is a different error message depending on whether or + # not pyopenssl is injected + assert ( + "No root certificates specified" in str(e.value.reason) + # PyPy sometimes uses all-caps here + or "certificate verify failed" in str(e.value.reason).lower() + or "invalid certificate chain" in str(e.value.reason) + ), ( + "Expected 'No root certificates specified', " + "'certificate verify failed', or " + "'invalid certificate chain', " + "instead got: %r" % e.value.reason + ) def test_no_ssl(self): with HTTPSConnectionPool(self.host, self.port) as pool: diff --git a/test/with_dummyserver/test_poolmanager.py b/test/with_dummyserver/test_poolmanager.py index a470113b..c7af72ba 100644 --- a/test/with_dummyserver/test_poolmanager.py +++ b/test/with_dummyserver/test_poolmanager.py @@ -83,7 +83,7 @@ def test_redirect_to_relative_url(self): def test_cross_host_redirect(self): with PoolManager() as http: cross_host_location = "%s/echo?a=b" % self.base_url_alt - try: + with pytest.raises(MaxRetryError): http.request( "GET", "%s/redirect" % self.base_url, @@ -91,12 +91,6 @@ def test_cross_host_redirect(self): timeout=1, retries=0, ) - self.fail( - "Request succeeded instead of raising an exception like it should." - ) - - except MaxRetryError: - pass r = http.request( "GET", @@ -110,8 +104,8 @@ def test_cross_host_redirect(self): def test_too_many_redirects(self): with PoolManager() as http: - try: - r = http.request( + with pytest.raises(MaxRetryError): + http.request( "GET", "%s/redirect" % self.base_url, fields={ @@ -120,14 +114,9 @@ def test_too_many_redirects(self): }, retries=1, ) - self.fail( - "Failed to raise MaxRetryError exception, returned %r" % r.status - ) - except MaxRetryError: - pass - try: - r = http.request( + with pytest.raises(MaxRetryError): + http.request( "GET", "%s/redirect" % self.base_url, fields={ @@ -136,11 +125,6 @@ def test_too_many_redirects(self): }, retries=Retry(total=None, redirect=1), ) - self.fail( - "Failed to raise MaxRetryError exception, returned %r" % r.status - ) - except MaxRetryError: - pass def test_redirect_cross_host_remove_headers(self): with PoolManager() as http: @@ -235,7 +219,7 @@ def test_raise_on_redirect(self): def test_raise_on_status(self): with PoolManager() as http: - try: + with pytest.raises(MaxRetryError): # the default is to raise r = http.request( "GET", @@ -243,13 +227,8 @@ def test_raise_on_status(self): fields={"status": "500 Internal Server Error"}, retries=Retry(total=1, status_forcelist=range(500, 600)), ) - self.fail( - "Failed to raise MaxRetryError exception, returned %r" % r.status - ) - except MaxRetryError: - pass - try: + with pytest.raises(MaxRetryError): # raise explicitly r = http.request( "GET", @@ -259,11 +238,6 @@ def test_raise_on_status(self): total=1, status_forcelist=range(500, 600), raise_on_status=True ), ) - self.fail( - "Failed to raise MaxRetryError exception, returned %r" % r.status - ) - except MaxRetryError: - pass # don't raise r = http.request( diff --git a/test/with_dummyserver/test_proxy_poolmanager.py b/test/with_dummyserver/test_proxy_poolmanager.py index d16a38a9..dde862ed 100644 --- a/test/with_dummyserver/test_proxy_poolmanager.py +++ b/test/with_dummyserver/test_proxy_poolmanager.py @@ -61,11 +61,9 @@ def test_proxy_conn_fail(self): with pytest.raises(MaxRetryError): http.request("GET", "%s/" % self.http_url) - try: + with pytest.raises(MaxRetryError) as e: http.request("GET", "%s/" % self.http_url) - self.fail("Failed to raise retry error.") - except MaxRetryError as e: - assert type(e.reason) == ProxyError + assert type(e.value.reason) == ProxyError def test_oldapi(self): with ProxyManager( @@ -82,14 +80,12 @@ def test_proxy_verified(self): self.proxy_url, cert_reqs="REQUIRED", ca_certs=DEFAULT_CA_BAD ) as http: https_pool = http._new_pool("https", self.https_host, self.https_port) - try: + with pytest.raises(MaxRetryError) as e: https_pool.request("GET", "/", retries=0) - self.fail("Didn't raise SSL error with wrong CA") - except MaxRetryError as e: - assert isinstance(e.reason, SSLError) - assert "certificate verify failed" in str(e.reason), ( - "Expected 'certificate verify failed', instead got: %r" % e.reason - ) + assert isinstance(e.value.reason, SSLError) + assert "certificate verify failed" in str(e.value.reason), ( + "Expected 'certificate verify failed', instead got: %r" % e.value.reason + ) http = proxy_from_url( self.proxy_url, cert_reqs="REQUIRED", ca_certs=DEFAULT_CA @@ -105,12 +101,10 @@ def test_proxy_verified(self): ) https_fail_pool = http._new_pool("https", "127.0.0.1", self.https_port) - try: + with pytest.raises(MaxRetryError) as e: https_fail_pool.request("GET", "/", retries=0) - self.fail("Didn't raise SSL invalid common name") - except MaxRetryError as e: - assert isinstance(e.reason, SSLError) - assert "doesn't match" in str(e.reason) + 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: @@ -135,7 +129,7 @@ def test_redirect(self): def test_cross_host_redirect(self): with proxy_from_url(self.proxy_url) as http: cross_host_location = "%s/echo?a=b" % self.http_url_alt - try: + with pytest.raises(MaxRetryError): http.request( "GET", "%s/redirect" % self.http_url, @@ -143,10 +137,6 @@ def test_cross_host_redirect(self): timeout=1, retries=0, ) - self.fail("We don't want to follow redirects here.") - - except MaxRetryError: - pass r = http.request( "GET", @@ -160,7 +150,7 @@ def test_cross_host_redirect(self): def test_cross_protocol_redirect(self): with proxy_from_url(self.proxy_url, ca_certs=DEFAULT_CA) as http: cross_protocol_location = "%s/echo?a=b" % self.https_url - try: + with pytest.raises(MaxRetryError): http.request( "GET", "%s/redirect" % self.http_url, @@ -168,10 +158,6 @@ def test_cross_protocol_redirect(self): timeout=1, retries=0, ) - self.fail("We don't want to follow redirects here.") - - except MaxRetryError: - pass r = http.request( "GET", @@ -334,11 +320,9 @@ def test_proxy_pooling_ext(self): @requires_network def test_https_proxy_timeout(self): with proxy_from_url("https://{host}".format(host=TARPIT_HOST)) as https: - try: + with pytest.raises(MaxRetryError) as e: https.request("GET", self.http_url, timeout=0.001) - self.fail("Failed to raise retry error.") - except MaxRetryError as e: - assert type(e.reason) == ConnectTimeoutError + assert type(e.value.reason) == ConnectTimeoutError @pytest.mark.timeout(0.5) @requires_network @@ -346,11 +330,9 @@ def test_https_proxy_pool_timeout(self): with proxy_from_url( "https://{host}".format(host=TARPIT_HOST), timeout=0.001 ) as https: - try: + with pytest.raises(MaxRetryError) as e: https.request("GET", self.http_url) - self.fail("Failed to raise retry error.") - except MaxRetryError as e: - assert type(e.reason) == ConnectTimeoutError + assert type(e.value.reason) == ConnectTimeoutError def test_scheme_host_case_insensitive(self): """Assert that upper-case schemes and hosts are normalized.""" diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py index 359e8755..6b7ed69e 100644 --- a/test/with_dummyserver/test_socketlevel.py +++ b/test/with_dummyserver/test_socketlevel.py @@ -229,16 +229,10 @@ def socket_handler(listener): with HTTPSConnectionPool( self.host, self.port, cert_reqs="REQUIRED", ca_certs=DEFAULT_CA ) as pool: - try: + with pytest.raises(MaxRetryError): pool.request("GET", "/", retries=0) - except MaxRetryError: done_receiving.set() - else: - done_receiving.set() - self.fail( - "Expected server to reject connection due to missing client " - "certificates" - ) + done_receiving.set() @requires_ssl_context_keyfile_password def test_client_cert_with_string_password(self): @@ -812,8 +806,7 @@ def socket_handler(listener): done_closing.set() # wait until the socket in our pool gets closed successful = complete.wait(timeout=1) - if not successful: - self.fail("Timed out waiting for connection close") + assert successful, "Timed out waiting for connection close" def test_release_conn_param_is_respected_after_timeout_retry(self): """For successful ```urlopen(release_conn=False)```, From 1733719676ea30f62dae29ab837c29b548108926 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 29 Oct 2019 16:23:38 +0400 Subject: [PATCH 05/19] Speed up queue test by using timeout=0 (#1711) Queue.get() accepts any non-negative number, so 0 works. It's also not arbitrary like 1 was, and the test suite now takes one less second to run! --- test/test_queue_monkeypatch.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/test_queue_monkeypatch.py b/test/test_queue_monkeypatch.py index b37af394..c07fb8ef 100644 --- a/test/test_queue_monkeypatch.py +++ b/test/test_queue_monkeypatch.py @@ -26,6 +26,6 @@ class TestMonkeypatchResistance(object): def test_queue_monkeypatching(self): with mock.patch.object(queue, "Empty", BadError): with HTTPConnectionPool(host="localhost", block=True) as http: - http._get_conn(timeout=1) + http._get_conn() with pytest.raises(EmptyPoolError): - http._get_conn(timeout=1) + http._get_conn(timeout=0) From 11d68efa7c150823472f0e5309c3a08a1a10c2f2 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 29 Oct 2019 16:54:17 +0400 Subject: [PATCH 06/19] Update urllib3/requests GitHub URLs (#1719) --- docs/index.rst | 2 +- dummyserver/server.py | 4 ++-- src/urllib3/connection.py | 2 +- src/urllib3/connectionpool.py | 2 +- src/urllib3/util/connection.py | 2 +- test/test_connectionpool.py | 4 ++-- test/test_queue_monkeypatch.py | 2 +- test/with_dummyserver/test_socketlevel.py | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 533b5822..42d306f8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -43,7 +43,7 @@ urllib3 can be installed with `pip <https://pip.pypa.io>`_:: Alternatively, you can grab the latest source code from `GitHub <https://github.com/urllib3/urllib3>`_:: - $ git clone git://github.com/shazow/urllib3.git + $ git clone git://github.com/urllib3/urllib3.git $ python setup.py install Usage diff --git a/dummyserver/server.py b/dummyserver/server.py index 1f899269..621a1dc3 100755 --- a/dummyserver/server.py +++ b/dummyserver/server.py @@ -86,7 +86,7 @@ def _has_ipv6(host): # has_ipv6 returns true if cPython was compiled with IPv6 support. # It does not tell us if the system has IPv6 support enabled. To # determine that we must bind to an IPv6 address. - # https://github.com/shazow/urllib3/pull/611 + # https://github.com/urllib3/urllib3/pull/611 # https://bugs.python.org/issue658327 try: sock = socket.socket(socket.AF_INET6) @@ -102,7 +102,7 @@ def _has_ipv6(host): # Some systems may have IPv6 support but DNS may not be configured # properly. We can not count that localhost will resolve to ::1 on all -# systems. See https://github.com/shazow/urllib3/pull/611 and +# systems. See https://github.com/urllib3/urllib3/pull/611 and # https://bugs.python.org/issue18792 HAS_IPV6_AND_DNS = _has_ipv6("localhost") HAS_IPV6 = _has_ipv6("::1") diff --git a/src/urllib3/connection.py b/src/urllib3/connection.py index 6bb5e45b..f5c946ad 100644 --- a/src/urllib3/connection.py +++ b/src/urllib3/connection.py @@ -412,7 +412,7 @@ def connect(self): ( "Certificate for {0} has no `subjectAltName`, falling back to check for a " "`commonName` for now. This feature is being removed by major browsers and " - "deprecated by RFC 2818. (See https://github.com/shazow/urllib3/issues/497 " + "deprecated by RFC 2818. (See https://github.com/urllib3/urllib3/issues/497 " "for details.)".format(hostname) ), SubjectAltNameWarning, diff --git a/src/urllib3/connectionpool.py b/src/urllib3/connectionpool.py index 26c4bd70..3fed831f 100644 --- a/src/urllib3/connectionpool.py +++ b/src/urllib3/connectionpool.py @@ -626,7 +626,7 @@ def urlopen( # # See issue #651 [1] for details. # - # [1] <https://github.com/shazow/urllib3/issues/651> + # [1] <https://github.com/urllib3/urllib3/issues/651> release_this_conn = release_conn # Merge the proxy headers. Only do this in HTTP. We have to copy the diff --git a/src/urllib3/util/connection.py b/src/urllib3/util/connection.py index 0e111262..86f0a3b0 100644 --- a/src/urllib3/util/connection.py +++ b/src/urllib3/util/connection.py @@ -121,7 +121,7 @@ def _has_ipv6(host): # has_ipv6 returns true if cPython was compiled with IPv6 support. # It does not tell us if the system has IPv6 support enabled. To # determine that we must bind to an IPv6 address. - # https://github.com/shazow/urllib3/pull/611 + # https://github.com/urllib3/urllib3/pull/611 # https://bugs.python.org/issue658327 try: sock = socket.socket(socket.AF_INET6) diff --git a/test/test_connectionpool.py b/test/test_connectionpool.py index db8d39fc..532d2445 100644 --- a/test/test_connectionpool.py +++ b/test/test_connectionpool.py @@ -285,7 +285,7 @@ def _test(exception, expect, reason=None): # The pool should never be empty, and with these two exceptions # being raised, a retry will be triggered, but that retry will # fail, eventually raising MaxRetryError, not EmptyPoolError - # See: https://github.com/shazow/urllib3/issues/76 + # See: https://github.com/urllib3/urllib3/issues/76 pool._make_request = lambda *args, **kwargs: _raise(HTTPException) with pytest.raises(MaxRetryError): pool.request("GET", "/", retries=1, pool_timeout=0.01) @@ -425,7 +425,7 @@ def test_release_conn_param_is_respected_after_http_error_retry(self): would be released if the initial request failed, even if a retry succeeded. - [1] <https://github.com/shazow/urllib3/issues/651> + [1] <https://github.com/urllib3/urllib3/issues/651> """ class _raise_once_make_request_function(object): diff --git a/test/test_queue_monkeypatch.py b/test/test_queue_monkeypatch.py index c07fb8ef..4ebad62b 100644 --- a/test/test_queue_monkeypatch.py +++ b/test/test_queue_monkeypatch.py @@ -20,7 +20,7 @@ class BadError(Exception): class TestMonkeypatchResistance(object): """ Test that connection pool works even with a monkey patched Queue module, - see obspy/obspy#1599, kennethreitz/requests#3742, shazow/urllib3#1061. + see obspy/obspy#1599, psf/requests#3742, urllib3/urllib3#1061. """ def test_queue_monkeypatching(self): diff --git a/test/with_dummyserver/test_socketlevel.py b/test/with_dummyserver/test_socketlevel.py index 6b7ed69e..b9c9ae5c 100644 --- a/test/with_dummyserver/test_socketlevel.py +++ b/test/with_dummyserver/test_socketlevel.py @@ -818,7 +818,7 @@ def test_release_conn_param_is_respected_after_timeout_retry(self): would be released if the initial request failed, even if a retry succeeded. - [1] <https://github.com/shazow/urllib3/issues/651> + [1] <https://github.com/urllib3/urllib3/issues/651> """ def socket_handler(listener): @@ -1237,7 +1237,7 @@ def request(): with pytest.raises(MaxRetryError) as cm: request() assert isinstance(cm.value.reason, SSLError) - # Should not hang, see https://github.com/shazow/urllib3/issues/529 + # Should not hang, see https://github.com/urllib3/urllib3/issues/529 with pytest.raises(MaxRetryError): request() From 3cff0a5620a1da136f08b4e1f5435e35528dd771 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Wed, 30 Oct 2019 16:22:24 +0400 Subject: [PATCH 07/19] Restore comment next to its intended line (#1720) --- test/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/__init__.py b/test/__init__.py index c89d0126..c9a9806a 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -127,8 +127,8 @@ def requires_network(test): def _is_unreachable_err(err): return getattr(err, "errno", None) in ( errno.ENETUNREACH, - errno.EHOSTUNREACH, - ) # For OSX + errno.EHOSTUNREACH, # For OSX + ) def _has_route(): try: From da5e605a24d0335c49d87928ae9614ca90e274a5 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Mon, 28 Oct 2019 15:43:02 +0400 Subject: [PATCH 08/19] Remove pkginfo which was only needed for twine It was introduced in https://github.com/urllib3/urllib3/pull/875, but since https://github.com/urllib3/urllib3/pull/1495 twine is no longer a dev requirement. --- dev-requirements.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 644aa48f..c585dcfa 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -3,7 +3,6 @@ coverage~=4.5 wheel==0.30.0 tornado==5.1.1 PySocks==1.6.8 -pkginfo==1.4.2 pytest-timeout==1.3.3 pytest==4.6.4 flaky==3.6.1 From c0a51cc0d7d63a68afd09e345439ccf02af9625b Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Mon, 28 Oct 2019 15:46:27 +0400 Subject: [PATCH 09/19] Remove lazy-object-proxy pin after upstream fix --- dev-requirements.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index c585dcfa..d4b2f313 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -7,9 +7,6 @@ pytest-timeout==1.3.3 pytest==4.6.4 flaky==3.6.1 -# https://github.com/ionelmc/python-lazy-object-proxy/issues/30 -lazy-object-proxy==1.4.0 - # https://github.com/GoogleCloudPlatform/python-repo-tools/issues/23 pylint<2.0;python_version<="2.7" From b2f2077fb5ba520df22fc77f80c2bc786cc44927 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Mon, 28 Oct 2019 15:57:45 +0400 Subject: [PATCH 10/19] Extract wheel from dev-requirements.txt Instead, install it on demand like twine. --- _travis/deploy.sh | 2 +- dev-requirements.txt | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/_travis/deploy.sh b/_travis/deploy.sh index 08f377ef..503f0723 100755 --- a/_travis/deploy.sh +++ b/_travis/deploy.sh @@ -2,6 +2,6 @@ set -exo pipefail -python3 -m pip install --upgrade twine +python3 -m pip install --upgrade twine wheel python3 setup.py sdist bdist_wheel python3 -m twine upload dist/* -u $PYPI_USERNAME -p $PYPI_PASSWORD --skip-existing diff --git a/dev-requirements.txt b/dev-requirements.txt index d4b2f313..d3f94d2b 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,6 +1,5 @@ mock==2.0.0 coverage~=4.5 -wheel==0.30.0 tornado==5.1.1 PySocks==1.6.8 pytest-timeout==1.3.3 From 3bc0baada5a02ccad636246168984602d2b9e24e Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Mon, 28 Oct 2019 15:55:56 +0400 Subject: [PATCH 11/19] Upgrade dev dependencies --- dev-requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index d3f94d2b..9926e3f0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,9 +1,9 @@ -mock==2.0.0 +mock==3.0.5 coverage~=4.5 tornado==5.1.1 PySocks==1.6.8 pytest-timeout==1.3.3 -pytest==4.6.4 +pytest==4.6.6 flaky==3.6.1 # https://github.com/GoogleCloudPlatform/python-repo-tools/issues/23 From 6c7e87c6e5127389d0776aa09479fdc6b473fac3 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 29 Oct 2019 16:33:57 +0400 Subject: [PATCH 12/19] Place pytest before the pytest plugins The idea is to make sure that we'll get the pytest version we want: the last that supports Python 2. Using a different version in Python 2 and Python 3 is possible, but is likely to lead to trouble in the future. --- dev-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 9926e3f0..36e42894 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -2,8 +2,8 @@ mock==3.0.5 coverage~=4.5 tornado==5.1.1 PySocks==1.6.8 -pytest-timeout==1.3.3 pytest==4.6.6 +pytest-timeout==1.3.3 flaky==3.6.1 # https://github.com/GoogleCloudPlatform/python-repo-tools/issues/23 From 9d2946995b4c34f661e2a1103fc03e28a32a6680 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Mon, 28 Oct 2019 16:34:44 +0400 Subject: [PATCH 13/19] Upgrade PySocks version --- dev-requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dev-requirements.txt b/dev-requirements.txt index 36e42894..7e3b6515 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -1,7 +1,9 @@ mock==3.0.5 coverage~=4.5 tornado==5.1.1 -PySocks==1.6.8 +PySocks==1.7.1 +# https://github.com/Anorov/PySocks/issues/131 +win-inet-pton==1.1.0 pytest==4.6.6 pytest-timeout==1.3.3 flaky==3.6.1 From 639dfda9bb042a433ad046b331868f42ab89d35a Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Wed, 30 Oct 2019 16:26:50 +0400 Subject: [PATCH 14/19] Remove unused certs and keys (#1721) --- dummyserver/certs/client.csr | 23 ----------------------- dummyserver/certs/client.key | 15 --------------- dummyserver/certs/client.pem | 21 --------------------- dummyserver/certs/intermediate.key | 15 --------------- dummyserver/certs/intermediate.pem | 18 ------------------ dummyserver/server.py | 8 -------- 6 files changed, 100 deletions(-) delete mode 100644 dummyserver/certs/client.csr delete mode 100644 dummyserver/certs/client.key delete mode 100644 dummyserver/certs/client.pem delete mode 100644 dummyserver/certs/intermediate.key delete mode 100644 dummyserver/certs/intermediate.pem diff --git a/dummyserver/certs/client.csr b/dummyserver/certs/client.csr deleted file mode 100644 index 703d3510..00000000 --- a/dummyserver/certs/client.csr +++ /dev/null @@ -1,23 +0,0 @@ ------BEGIN CERTIFICATE----- -MIID1TCCAz6gAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMCRkkx -DjAMBgNVBAgTBWR1bW15MQ4wDAYDVQQHEwVkdW1teTEOMAwGA1UEChMFZHVtbXkx -DjAMBgNVBAsTBWR1bW15MREwDwYDVQQDEwhTbmFrZU9pbDEfMB0GCSqGSIb3DQEJ -ARYQZHVtbXlAdGVzdC5sb2NhbDAeFw0xMTEyMjIwNzU5NTlaFw0yMTEyMTgwNzU5 -NTlaMH8xCzAJBgNVBAYTAkZJMQ4wDAYDVQQIEwVkdW1teTEOMAwGA1UEBxMFZHVt -bXkxDjAMBgNVBAoTBWR1bW15MQ4wDAYDVQQLEwVkdW1teTEPMA0GA1UEAxMGY2xp -ZW50MR8wHQYJKoZIhvcNAQkBFhBjbGllbnRAbG9jYWxob3N0MIGfMA0GCSqGSIb3 -DQEBAQUAA4GNADCBiQKBgQDaITA/XCzviqjex+lJJP+pgmQQ+ncUf+PDaFw86kWh -cWuI2eSBVaIaP6SsxYgIODQTjqYGjRogsd1Nvx3gRdIMEagTfVQyVwfDfNp8aT8v -SY/wDYFjsD07asmjGvwiu0sLp4t/tMz+x5ELlU4+hGnmPInH6hLK150DqgbNmJus -3wIDAQABo4IBXDCCAVgwCQYDVR0TBAIwADARBglghkgBhvhCAQEEBAMCBLAwKwYJ -YIZIAYb4QgENBB4WHFRpbnlDQSBHZW5lcmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0O -BBYEFG71FCU2yisH1GyrcqYaPKVeTWxBMIG2BgNVHSMEga4wgauAFBl3fyNiYkJZ -Rft1ncdzcgS7MwotoYGHpIGEMIGBMQswCQYDVQQGEwJGSTEOMAwGA1UECBMFZHVt -bXkxDjAMBgNVBAcTBWR1bW15MQ4wDAYDVQQKEwVkdW1teTEOMAwGA1UECxMFZHVt -bXkxETAPBgNVBAMTCFNuYWtlT2lsMR8wHQYJKoZIhvcNAQkBFhBkdW1teUB0ZXN0 -LmxvY2FsggkAs+uxyi/hv+MwCQYDVR0SBAIwADAbBgNVHREEFDASgRBjbGllbnRA -bG9jYWxob3N0MAsGA1UdDwQEAwIFoDANBgkqhkiG9w0BAQUFAAOBgQDEwZmp3yE8 -R4U9Ob/IeEo6O3p0T4o7GNvufGksM/mELmzyC+Qh/Ul6fNn+IhdKWpo61sMZou+n -eOufXVouc8dGhQ1Qi5s0i51d/ouhfYNs+AGRcpwEieVjZhgE1XfrNwvvjIx3yPtK -m9LSmCtVKcTWqOHQywKn+G83a+7bsh835Q== ------END CERTIFICATE----- diff --git a/dummyserver/certs/client.key b/dummyserver/certs/client.key deleted file mode 100644 index 0d1c3434..00000000 --- a/dummyserver/certs/client.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICWwIBAAKBgQDaITA/XCzviqjex+lJJP+pgmQQ+ncUf+PDaFw86kWhcWuI2eSB -VaIaP6SsxYgIODQTjqYGjRogsd1Nvx3gRdIMEagTfVQyVwfDfNp8aT8vSY/wDYFj -sD07asmjGvwiu0sLp4t/tMz+x5ELlU4+hGnmPInH6hLK150DqgbNmJus3wIDAQAB -AoGAKMMg+AYqo4z+57rl/nQ6jpu+RWn4zMzlbEPZUMzavEOsu8M0L3MoOs1/4YV8 -WUTffnQe1ISTyF5Uo82+MIX7rUtfJITFSQrIWe7AGdm6Nir8TQQ7fD97modXyAUx -69I9SQjQlseg5PCRCp/DfcBncvHeYuf8gAJK5FfC1VW1cQECQQDvzFNoGrwnsrtm -4gj1Kt0c20jkIYFN6iQ6Sjs/1fk1cXDeWzjPaa92zF+i+02Ma/eWJ0ZVrhisw6sv -zxGp+ByBAkEA6N4SpuGWytJqCRfwenQZ4Oa8mNcVo5ulGf/eUHVXvHewWxQ7xWRi -iWUj/z1byR9+yno8Yfd04kaNCPYN/ICZXwJAAf5//xCh2e6pkkx06J0Ho7LLI2KH -8b7tuDJf1cMQxHoCB0dY7JijZeiDLxbJ6U4IjA4djp7ZA67I4KfnLLOsgQJARLZS -dp+WKR7RXwGLWfasNCqhd8/veKlSnEtdxAv76Ya/qQBdaq9mS/hmGMh4Lu52MTTE -YHvuJ159+yjvk5Q2rQJABjlU1+GZqwv/7QM7GxfJO+GPI4PHv5Yji5s7LLu2c6dL -XY2XiTHQL9PnPrKp3+qDDzxjyej30lfz4he6E5pI+g== ------END RSA PRIVATE KEY----- diff --git a/dummyserver/certs/client.pem b/dummyserver/certs/client.pem deleted file mode 100644 index c8302bf2..00000000 --- a/dummyserver/certs/client.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDczCCAtygAwIBAgIBATANBgkqhkiG9w0BAQUFADCBgTELMAkGA1UEBhMCRkkx -DjAMBgNVBAgMBWR1bW15MQ4wDAYDVQQHDAVkdW1teTEOMAwGA1UECgwFZHVtbXkx -DjAMBgNVBAsMBWR1bW15MREwDwYDVQQDDAhTbmFrZU9pbDEfMB0GCSqGSIb3DQEJ -ARYQZHVtbXlAdGVzdC5sb2NhbDAeFw0xMTEyMjIwNzU4NDBaFw0yMTEyMTgwNzU4 -NDBaMGExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIDAVkdW1teTEOMAwGA1UEBwwFZHVt -bXkxDjAMBgNVBAoMBWR1bW15MQ4wDAYDVQQLDAVkdW1teTESMBAGA1UEAwwJbG9j -YWxob3N0MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDXe3FqmCWvP8XPxqtT -+0bfL1Tvzvebi46k0WIcUV8bP3vyYiSRXG9ALmyzZH4GHY9UVs4OEDkCMDOBSezB -0y9ai/9doTNcaictdEBu8nfdXKoTtzrn+VX4UPrkH5hm7NQ1fTQuj1MR7yBCmYqN -3Q2Q+Efuujyx0FwBzAuy1aKYuwIDAQABo4IBGDCCARQwCQYDVR0TBAIwADAdBgNV -HQ4EFgQUG+dK5Uos08QUwAWofDb3a8YcYlIwgbYGA1UdIwSBrjCBq4AUGXd/I2Ji -QllF+3Wdx3NyBLszCi2hgYekgYQwgYExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIDAVk -dW1teTEOMAwGA1UEBwwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MQ4wDAYDVQQLDAVk -dW1teTERMA8GA1UEAwwIU25ha2VPaWwxHzAdBgkqhkiG9w0BCQEWEGR1bW15QHRl -c3QubG9jYWyCCQCz67HKL+G/4zAJBgNVHRIEAjAAMCQGA1UdEQQdMBuBDnJvb3RA -bG9jYWxob3N0gglsb2NhbGhvc3QwDQYJKoZIhvcNAQEFBQADgYEAgcW6X1ZUyufm -TFEqEAdpKXdL0rxDwcsM/qqqsXbkz17otH6ujPhBEagzdKtgeNKfy0aXz6rWZugk -lF0IqyC4mcI+vvfgGR5Iy4KdXMrIX98MbrvGJBfbdKhGW2b84wDV42DIDiD2ZGGe -6YZQQIo9LxjuOTf9jsvf+PIkbI4H0To= ------END CERTIFICATE----- diff --git a/dummyserver/certs/intermediate.key b/dummyserver/certs/intermediate.key deleted file mode 100644 index 0b6cd739..00000000 --- a/dummyserver/certs/intermediate.key +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIICXAIBAAKBgQDEBOFnQ7PU5TqKefxH0VQZ0gxf1s8MrtvCHw2DouX+dAmgebcx -IBo3I4WlR2IOqw0x5Nr9/zfNoO8UEVioVvhsZXXaTCYzm4jjs4bx912pve8RH/q5 -Z8pdHLoUW8HDR9xiJuoROX/0KmD7LWAbj+kmZVCdA0EGoNp//bgC6Ol/uwIDAQAB -AoGAO76dEQNioWYItMI/cYhM0N3jpaZsTxpQotciIFgbL7YgZQgUHOYC94FdL6YV -LhFWoTl2wenzETqXBA/RbOWtK5wmpmu4Qy9Bq+r7FW296lyYOYyjuwz8AJD2kOTe -A9VjTJ9YBGxcbyI3/sSfF3tyNSyQZoh2AEIhsbKEmJPByzkCQQDhLbpvjkMAK5hq -P6TB4IwgK/dAF6fHdlDFCNfwhBvb1LiRe2OJMuPuAGlx+sMgiEiMntKoJrlE09NB -SnWnlaeHAkEA3tlmR+6GsEt6Quv4KgBMlJdVH/AYCba5eX/Ru5kz4WJwLCqJ5sik -V5wPt+lmgAsOLbivKXwuhb4p9WrBtOXLLQJANYOflhlyFN1HeKCtcCIESzUHqqS0 -i/OzWFA0uYU79a+FOZXgXt/ISWyxopPcwaOB0mGAYNPrHc9VmmOuuGgZiwJALFnR -/FDhZ2auH3F9A0bp9syjeWa8Mfq2sRKaOB7Gb326219f8JlP88uwaSa/ao5ItRrD -aZs4Ww+8pAYqJQlyxQJBANtJ14x0aOeFPY4MD7JdqRKxiQF4g9lL149yRGd+mRQC -OEonvmOrib8hGE6ivElMR3azEWCC3BcoKUxdplkY+Xw= ------END RSA PRIVATE KEY----- diff --git a/dummyserver/certs/intermediate.pem b/dummyserver/certs/intermediate.pem deleted file mode 100644 index 1e789cd7..00000000 --- a/dummyserver/certs/intermediate.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIC3jCCAkegAwIBAgImMUFZJlNYl5MjhGJkM4MnlQKIQZcWk5k3UQWCCXSURZIw -eBZAYoYwDQYJKoZIhvcNAQELBQAwgYExCzAJBgNVBAYTAkZJMQ4wDAYDVQQIDAVk -dW1teTEOMAwGA1UEBwwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MQ4wDAYDVQQLDAVk -dW1teTERMA8GA1UEAwwIU25ha2VPaWwxHzAdBgkqhkiG9w0BCQEWEGR1bW15QHRl -c3QubG9jYWwwHhcNMTcwNTEyMTgyMDUyWhcNMjExMjE5MTgyMDUyWjBxMQswCQYD -VQQGEwJGSTEOMAwGA1UECAwFZHVtbXkxDjAMBgNVBAoMBWR1bW15MQ4wDAYDVQQL -DAVkdW1teTERMA8GA1UEAwwIU25ha2VPaWwxHzAdBgkqhkiG9w0BCQEWEGR1bW15 -QHRlc3QubG9jYWwwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMQE4WdDs9Tl -Oop5/EfRVBnSDF/Wzwyu28IfDYOi5f50CaB5tzEgGjcjhaVHYg6rDTHk2v3/N82g -7xQRWKhW+GxlddpMJjObiOOzhvH3Xam97xEf+rlnyl0cuhRbwcNH3GIm6hE5f/Qq -YPstYBuP6SZlUJ0DQQag2n/9uALo6X+7AgMBAAGjUDBOMB0GA1UdDgQWBBSeW2ye -6HaaO2qoNaTZE1LALueMeTAfBgNVHSMEGDAWgBQZd38jYmJCWUX7dZ3Hc3IEuzMK -LTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4GBAGnXyMzPPe5o4tYasY0K -A9sgxg42rH1gAeDJXeG4QqLoVi9JKbOBXdJGN9ZWD9K4EASknwWsa0TWSv291jHN -4+Uz8bHZ+4mH5HMpXZPsHorHR2te2XCZGMNE1V/1N0Q8qQk8CoxDSl5l5n67W9DY -iTQB1g/ymK3/hnTohqkFj9xd ------END CERTIFICATE----- diff --git a/dummyserver/server.py b/dummyserver/server.py index 621a1dc3..fdf95ec9 100755 --- a/dummyserver/server.py +++ b/dummyserver/server.py @@ -37,14 +37,6 @@ DEFAULT_CLIENT_CERTS = { "certfile": os.path.join(CERTS_PATH, "client_intermediate.pem"), "keyfile": os.path.join(CERTS_PATH, "client_intermediate.key"), - "subject": dict( - countryName=u"FI", - stateOrProvinceName=u"dummy", - organizationName=u"dummy", - organizationalUnitName=u"dummy", - commonName=u"SnakeOilClient", - emailAddress=u"dummy@test.local", - ), } DEFAULT_CLIENT_NO_INTERMEDIATE_CERTS = { "certfile": os.path.join(CERTS_PATH, "client_no_intermediate.pem"), From 0e31f599aa8a990ec904dcf249b026ef3f87af17 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Wed, 30 Oct 2019 10:27:38 +0400 Subject: [PATCH 15/19] Switch noxfile to Unix line endings --- noxfile.py | 204 ++++++++++++++++++++++++++--------------------------- 1 file changed, 102 insertions(+), 102 deletions(-) diff --git a/noxfile.py b/noxfile.py index 33090226..d808d466 100644 --- a/noxfile.py +++ b/noxfile.py @@ -1,102 +1,102 @@ -import os -import shutil - -import nox - - -def tests_impl(session, extras="socks,secure,brotli"): - # Install deps and the package itself. - session.install("-r", "dev-requirements.txt") - session.install(".[{extras}]".format(extras=extras)) - - # Show the pip version. - session.run("pip", "--version") - # Print the Python version and bytesize. - session.run("python", "--version") - session.run("python", "-c", "import struct; print(struct.calcsize('P') * 8)") - # Print OpenSSL information. - session.run("python", "-m", "OpenSSL.debug") - - # Inspired from https://github.com/pyca/cryptography - # We use parallel mode and then combine here so that coverage.py will take - # the paths like .tox/pyXY/lib/pythonX.Y/site-packages/urllib3/__init__.py - # and collapse them into src/urllib3/__init__.py. - - session.run( - "coverage", - "run", - "--parallel-mode", - "-m", - "pytest", - "-r", - "sx", - "--no-success-flaky-report", - *(session.posargs or ("test/",)), - env={"PYTHONWARNINGS": "always::DeprecationWarning"} - ) - session.run("coverage", "combine") - session.run("coverage", "report", "-m") - - -@nox.session(python=["2.7", "3.4", "3.5", "3.6", "3.7", "3.8", "pypy"]) -def test(session): - tests_impl(session) - - -@nox.session(python=["2", "3"]) -def google_brotli(session): - # https://pypi.org/project/Brotli/ is the Google version of brotli, so - # install it separately and don't install our brotli extra (which installs - # brotlipy). - session.install("brotli") - tests_impl(session, extras="socks,secure") - - -@nox.session(python="2.7") -def app_engine(session): - session.install("-r", "dev-requirements.txt") - session.install(".") - session.run( - "coverage", - "run", - "--parallel-mode", - "-m", - "pytest", - "-r", - "sx", - "test/appengine", - *session.posargs - ) - session.run("coverage", "combine") - session.run("coverage", "report", "-m") - - -@nox.session() -def blacken(session): - """Run black code formater.""" - session.install("black") - session.run("black", "src", "dummyserver", "test", "noxfile.py", "setup.py") - - lint(session) - - -@nox.session -def lint(session): - session.install("flake8", "black") - session.run("flake8", "--version") - session.run("black", "--version") - session.run( - "black", "--check", "src", "dummyserver", "test", "noxfile.py", "setup.py" - ) - session.run("flake8", "setup.py", "docs", "dummyserver", "src", "test") - - -@nox.session -def docs(session): - session.install("-r", "docs/requirements.txt") - session.install(".[socks,secure,brotli]") - - session.chdir("docs") - if os.path.exists("_build"): - shutil.rmtree("_build") - session.run("sphinx-build", "-W", ".", "_build/html") +import os +import shutil + +import nox + + +def tests_impl(session, extras="socks,secure,brotli"): + # Install deps and the package itself. + session.install("-r", "dev-requirements.txt") + session.install(".[{extras}]".format(extras=extras)) + + # Show the pip version. + session.run("pip", "--version") + # Print the Python version and bytesize. + session.run("python", "--version") + session.run("python", "-c", "import struct; print(struct.calcsize('P') * 8)") + # Print OpenSSL information. + session.run("python", "-m", "OpenSSL.debug") + + # Inspired from https://github.com/pyca/cryptography + # We use parallel mode and then combine here so that coverage.py will take + # the paths like .tox/pyXY/lib/pythonX.Y/site-packages/urllib3/__init__.py + # and collapse them into src/urllib3/__init__.py. + + session.run( + "coverage", + "run", + "--parallel-mode", + "-m", + "pytest", + "-r", + "sx", + "--no-success-flaky-report", + *(session.posargs or ("test/",)), + env={"PYTHONWARNINGS": "always::DeprecationWarning"} + ) + session.run("coverage", "combine") + session.run("coverage", "report", "-m") + + +@nox.session(python=["2.7", "3.4", "3.5", "3.6", "3.7", "3.8", "pypy"]) +def test(session): + tests_impl(session) + + +@nox.session(python=["2", "3"]) +def google_brotli(session): + # https://pypi.org/project/Brotli/ is the Google version of brotli, so + # install it separately and don't install our brotli extra (which installs + # brotlipy). + session.install("brotli") + tests_impl(session, extras="socks,secure") + + +@nox.session(python="2.7") +def app_engine(session): + session.install("-r", "dev-requirements.txt") + session.install(".") + session.run( + "coverage", + "run", + "--parallel-mode", + "-m", + "pytest", + "-r", + "sx", + "test/appengine", + *session.posargs + ) + session.run("coverage", "combine") + session.run("coverage", "report", "-m") + + +@nox.session() +def blacken(session): + """Run black code formater.""" + session.install("black") + session.run("black", "src", "dummyserver", "test", "noxfile.py", "setup.py") + + lint(session) + + +@nox.session +def lint(session): + session.install("flake8", "black") + session.run("flake8", "--version") + session.run("black", "--version") + session.run( + "black", "--check", "src", "dummyserver", "test", "noxfile.py", "setup.py" + ) + session.run("flake8", "setup.py", "docs", "dummyserver", "src", "test") + + +@nox.session +def docs(session): + session.install("-r", "docs/requirements.txt") + session.install(".[socks,secure,brotli]") + + session.chdir("docs") + if os.path.exists("_build"): + shutil.rmtree("_build") + session.run("sphinx-build", "-W", ".", "_build/html") From cfcf6e2d9d66b21d8c51cfdc51c4189ba42e466b Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Wed, 30 Oct 2019 10:28:35 +0400 Subject: [PATCH 16/19] Show failed tests in pytest report It turns out that without turning this one it's difficult to list exactly the tests that failed. --- noxfile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/noxfile.py b/noxfile.py index d808d466..a1b7608c 100644 --- a/noxfile.py +++ b/noxfile.py @@ -29,7 +29,7 @@ def tests_impl(session, extras="socks,secure,brotli"): "-m", "pytest", "-r", - "sx", + "a", "--no-success-flaky-report", *(session.posargs or ("test/",)), env={"PYTHONWARNINGS": "always::DeprecationWarning"} From cf0eda9b45579376b121406bbea2e866f1a1c533 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Wed, 30 Oct 2019 10:30:06 +0400 Subject: [PATCH 17/19] Tell pytest to show native tracebacks The default pytest tracebacks are really verbose: for each function in the stack they include the whole function until the exception, and the representation of its parameters. On the other hand, native tracebacks are way more concise, showing only two lines for each function and exception chains. The benefits of switching to native tracebacks: * The useful information fits in one screen * Python trains us to read those tracebacks from day one * We avoid the verbosity of showing the source code: we can already see it in GitHub and our editors * While we lose the information about parameters, I've never found this information to be useful I've been using this configuration for a few months now and never looked back! --- noxfile.py | 1 + 1 file changed, 1 insertion(+) diff --git a/noxfile.py b/noxfile.py index a1b7608c..2600c2bb 100644 --- a/noxfile.py +++ b/noxfile.py @@ -30,6 +30,7 @@ def tests_impl(session, extras="socks,secure,brotli"): "pytest", "-r", "a", + "--tb=native", "--no-success-flaky-report", *(session.posargs or ("test/",)), env={"PYTHONWARNINGS": "always::DeprecationWarning"} From 18aebad153553b53b75c6e65177f5c1dc2f9b457 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 12 Nov 2019 10:18:19 +0400 Subject: [PATCH 18/19] Fix lint --- test/test_sync_connection.py | 4 ++-- test/with_dummyserver/test_chunked_transfer.py | 1 - test/with_dummyserver/test_no_ssl.py | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/test/test_sync_connection.py b/test/test_sync_connection.py index 1ff15064..3c6be18a 100644 --- a/test/test_sync_connection.py +++ b/test/test_sync_connection.py @@ -136,7 +136,7 @@ def send(self, data): if event is not EVENT_SEND: raise ScenarioError("Expected EVENT_SEND, got %s" % event) - amount, = args + (amount,) = args self._raise_errors(amount) if amount is SEND_ALL: amount = len(data) @@ -152,7 +152,7 @@ def recv(self, amt): if event is not EVENT_RECV: raise ScenarioError("Expected EVENT_RECV, got %s" % event) - amount, = args + (amount,) = args self._raise_errors(amount) if amount is RECV_ALL: amount = min(len(RESPONSE), amt) diff --git a/test/with_dummyserver/test_chunked_transfer.py b/test/with_dummyserver/test_chunked_transfer.py index 23316d5b..48176f78 100644 --- a/test/with_dummyserver/test_chunked_transfer.py +++ b/test/with_dummyserver/test_chunked_transfer.py @@ -6,7 +6,6 @@ from urllib3.exceptions import InvalidBodyError from urllib3.util.retry import Retry from dummyserver.testcase import SocketDummyServerTestCase, consume_socket -import pytest # Retry failed tests pytestmark = pytest.mark.flaky diff --git a/test/with_dummyserver/test_no_ssl.py b/test/with_dummyserver/test_no_ssl.py index 292812a4..59d0bd50 100644 --- a/test/with_dummyserver/test_no_ssl.py +++ b/test/with_dummyserver/test_no_ssl.py @@ -8,7 +8,6 @@ from dummyserver.testcase import HTTPDummyServerTestCase, HTTPSDummyServerTestCase -import pytest import urllib3 # Retry failed tests From f331ec49a9b102a3e1eec7924fad35d0eb236163 Mon Sep 17 00:00:00 2001 From: Quentin Pradet <quentin.pradet@gmail.com> Date: Tue, 12 Nov 2019 10:21:45 +0400 Subject: [PATCH 19/19] appveyor: fix broken build --- dev-requirements.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev-requirements.txt b/dev-requirements.txt index 87587a58..9d391618 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -18,5 +18,7 @@ gcp-devrel-py-tools # optional dependencies, only intended for use with Python 3.5+ trio==0.3.0; python_version >= "3.5" +# https://github.com/mhammond/pywin32/issues/1439 +pywin32!=226; python_version >= "3.5" and os_name == 'nt' twisted[tls]==19.2.0; python_version >= "3.5" and os_name != 'nt' twisted[tls,windows_platform]==19.2.0; python_version >= "3.5" and os_name == 'nt'