diff --git a/.github/workflows/tox.yml b/.github/workflows/tox.yml index 280a7e5b..30f9f349 100644 --- a/.github/workflows/tox.yml +++ b/.github/workflows/tox.yml @@ -32,6 +32,8 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python }} + env: + PIP_TRUSTED_HOST: "pypi.python.org pypi.org files.pythonhosted.org" - name: Install dependencies run: | sudo apt update diff --git a/requirements.devel.txt b/requirements.devel.txt index 5ea0a4f6..cea4f609 100644 --- a/requirements.devel.txt +++ b/requirements.devel.txt @@ -1 +1 @@ -tox==3.14.3 +tox==3.28.0 diff --git a/requirements.docs.txt b/requirements.docs.txt index 8458faf2..7628b13e 100644 --- a/requirements.docs.txt +++ b/requirements.docs.txt @@ -1,4 +1 @@ -Sphinx==2.4.4 -docutils==0.16 -jinja2<3.1 # We need an older version due to compatilibility -markupsafe<2.1 # Needed because of jinja2 +Sphinx==7.0.1 diff --git a/requirements.tests.txt b/requirements.tests.txt index d36d68c7..809d8914 100644 --- a/requirements.tests.txt +++ b/requirements.tests.txt @@ -16,7 +16,8 @@ setuptools==44.0.0;python_version<"3.10" setuptools>64;python_version>="3.10" coverage==5.0.3;python_version<"3.10" coverage>=6;python_version>="3.10" -flaky==3.6.1 +flaky==3.7.0;python_version<"3.6" +flaky==3.8.1;python_version>="3.6" # for integration tests # eventlet is pinned until https://github.com/benoitc/gunicorn/pull/2581 diff --git a/setup.cfg b/setup.cfg index 32977d4b..5e74955c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -51,7 +51,9 @@ install_requires = contextvars~=2.4;python_version>="3.5" and python_version<"3.7" [options.extras_require] -gunicorn = gunicorn>=19.7.0 +gunicorn = + gunicorn>=19.7.0;python_version>"3.6" + gunicorn==19.7.0,<21.0;python_version>="3.5" and python_version<"3.8" raven = raven>=6.4.0 celery = celery~=4.4;python_version~="3.5.0" diff --git a/setup.py b/setup.py index 26576dbe..5668f57a 100644 --- a/setup.py +++ b/setup.py @@ -160,7 +160,8 @@ 'gevent>=20.9.0', ], gunicorn=[ - 'gunicorn>=19.7.0', + 'gunicorn>=19.7.0;python_version>"3.6"', + 'gunicorn>=19.7.0,<21.0;python_version>="3.5" and python_version<"3.8"', ], pg=[ 'sqlparse>=0.4.2', diff --git a/tests/test_requests.py b/tests/test_requests.py index 11f3448e..156e67dc 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -44,6 +44,15 @@ import talisker.statsd import talisker.testing +try: + # Compatible urllib3 HTTPResponses subclass their Base class. + URLLIB3_COMPATIBLE_VERSION = issubclass( + urllib3.response.HTTPResponse, + urllib3.response.BaseHTTPResponse + ) +except AttributeError: + URLLIB3_COMPATIBLE_VERSION = False + def request(method='GET', host='http://example.com', url='/', **kwargs): req = requests.Request(method, url=host + url, **kwargs) @@ -481,16 +490,36 @@ def set_responses(self, responses): self.response_iter = iter(responses) def make_response(self, content, status='200 OK', headers={}): - """Make a fake http.client.HTTPResponse based on a byte stream.""" + """Make a fake HTTPResponse based on a byte stream. + + For versions of urllib3 where urllib3.response.HTTPResponse is + not API-compatible with http.client.HTTPResponse, we return the + http.client version. For versions after, we return a + urllib3.response.HTTPResponse. + """ + if not headers: + headers["Content-Type"] = "text/html" + formatted_headers = '\r\n'.join( '{}: {}'.format(k, v) for k, v in headers.items() ) - stream = 'HTTP/1.1 {}\r\n{}\r\n{}'.format( + stream = 'HTTP/1.1 {}\r\n{}\r\n\r\n{}'.format( status, formatted_headers, content, ) sock = FakeSocket(stream.encode('utf8')) - response = http.client.HTTPResponse(sock) - response.begin() # parse the stream + http_response = http.client.HTTPResponse(sock) + http_response.begin() + + if not URLLIB3_COMPATIBLE_VERSION: + return http_response + + response = urllib3.response.HTTPResponse( + body=http_response, + headers=headers, + status=http_response.status, + preload_content=False, + original_response=http_response, + ) return response def make_request(self, pool, conn, method, url, **kwargs): @@ -688,7 +717,8 @@ def test_adapter_exceptions_match_default(mock_urllib3, retry, response): exc = None try: - session.get('http://default/') + response = session.get('http://default/') + response.raise_for_status() except Exception as e: exc = e