From d3a4d06238ee418ac122368515c71db47e329058 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Tue, 30 May 2017 11:49:05 +0100 Subject: [PATCH 01/13] Begin dev section of README --- HISTORY.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index c7f82e03..d323d18c 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,11 @@ Release History --------------- +dev ++++ + +**Bugfixes** + 2.17.3 (2017-05-29) +++++++++++++++++++ From 98bbf8d17088cc9e11b30038e451cbafba1ef7e2 Mon Sep 17 00:00:00 2001 From: Jacob Mansfield Date: Tue, 30 May 2017 11:01:34 +0100 Subject: [PATCH 02/13] Catch error if multiprocessing is not available or is unable to determine the number of CPUs --- HISTORY.rst | 4 ++++ setup.py | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index d323d18c..edd07ae1 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -8,6 +8,10 @@ dev **Bugfixes** +- Resolve installation failure if multiprocessing is not available +- Resolve tests crash if multiprocessing is not able to determine the number of CPU cores + + 2.17.3 (2017-05-29) +++++++++++++++++++ diff --git a/setup.py b/setup.py index 193c8877..93a85077 100755 --- a/setup.py +++ b/setup.py @@ -8,7 +8,6 @@ from codecs import open from setuptools import setup from setuptools.command.test import test as TestCommand -from multiprocessing import cpu_count here = os.path.abspath(os.path.dirname(__file__)) @@ -17,7 +16,11 @@ class PyTest(TestCommand): def initialize_options(self): TestCommand.initialize_options(self) - self.pytest_args = ['-n', str(cpu_count()), '--boxed'] + try: + from multiprocessing import cpu_count + self.pytest_args = ['-n', str(cpu_count()), '--boxed'] + except (ImportError, NotImplementedError): + self.pytest_args = ['-n', '1', '--boxed'] def finalize_options(self): TestCommand.finalize_options(self) From 0abb69b1cb3fbad9db7b52be363e939673c5c429 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Tue, 6 Jun 2017 12:01:41 +0800 Subject: [PATCH 03/13] Fix a typo: verison -> version --- requests/help.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/help.py b/requests/help.py index ac0691e3..695b5ee2 100644 --- a/requests/help.py +++ b/requests/help.py @@ -23,7 +23,7 @@ else: def _implementation(): - """Return a dict with the Python implementation and verison. + """Return a dict with the Python implementation and version. Provide both the name and the version of the Python implementation currently running. For example, on CPython 2.7.5 it will return From 5a616ee980ae2cc9d42f8d0221420ad9eed4b2e0 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Tue, 6 Jun 2017 22:44:22 +0100 Subject: [PATCH 04/13] Add the pytest .cache/ directory to .gitignore For example, when a pytest run fails, it saves failure metadata to: `.cache/v/cache/lastfailed` --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index b2abf8c2..19ebfd79 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,7 @@ nosetests.xml junit-report.xml pylint.txt toy.py +.cache/ cover/ build/ docs/_build From 4847f5b8cd4071099f06e178856bc9e1b9193b2e Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Tue, 6 Jun 2017 23:26:07 +0100 Subject: [PATCH 05/13] Allow Requests.Response to be used as a context manager This saves having to wrap the call to requests with `contextlib.closing()`, allowing it to be used directly in a `with` statement, like so: ``` with requests.get('http://httpbin.org/get', stream=True) as r: # Do things with the response here. ``` Fixes #4136. --- AUTHORS.rst | 1 + HISTORY.rst | 5 +++++ docs/user/advanced.rst | 8 ++------ requests/models.py | 6 ++++++ tests/test_requests.py | 6 ++++++ 5 files changed, 20 insertions(+), 6 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 7e0bddf0..cda8e223 100755 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -183,3 +183,4 @@ Patches and Suggestions - Shmuel Amar (`@shmuelamar `_) - Gary Wu (`@garywu `_) - Ryan Pineo (`@ryanpineo `_) +- Ed Morley (`@edmorley `_) diff --git a/HISTORY.rst b/HISTORY.rst index edd07ae1..865169a5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,6 +6,11 @@ Release History dev +++ +**Improvements** + +- ``Response`` is now a context manager, so can be used directly in a `with` statement + without first having to be wrapped by ``contextlib.closing()``. + **Bugfixes** - Resolve installation failure if multiprocessing is not available diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst index 2aac434c..a1b68707 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -301,15 +301,11 @@ release the connection back to the pool unless you consume all the data or call :meth:`Response.close `. This can lead to inefficiency with connections. If you find yourself partially reading request bodies (or not reading them at all) while using ``stream=True``, you should -consider using ``contextlib.closing`` (`documented here`_), like this:: +make the request within a ``with`` statement to ensure it's always closed:: - from contextlib import closing - - with closing(requests.get('http://httpbin.org/get', stream=True)) as r: + with requests.get('http://httpbin.org/get', stream=True) as r: # Do things with the response here. -.. _`documented here`: http://docs.python.org/2/library/contextlib.html#contextlib.closing - .. _keep-alive: Keep-Alive diff --git a/requests/models.py b/requests/models.py index 1375a3af..2148024f 100644 --- a/requests/models.py +++ b/requests/models.py @@ -634,6 +634,12 @@ class Response(object): #: is a response. self.request = None + def __enter__(self): + return self + + def __exit__(self, *args): + self.close() + def __getstate__(self): # Consume everything; accessing the content attribute makes # sure the content has been fully read. diff --git a/tests/test_requests.py b/tests/test_requests.py index b8350cb7..365ffcb2 100755 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -1668,6 +1668,12 @@ class TestRequests: next(it) assert len(list(it)) == 3 + def test_response_context_manager(self, httpbin): + with requests.get(httpbin('stream/4'), stream=True) as response: + assert isinstance(response, requests.Response) + + assert response.raw.closed + def test_unconsumed_session_response_closes_connection(self, httpbin): s = requests.session() From f8ccc604987cdad57426b2852951402734b994bb Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Wed, 7 Jun 2017 13:50:00 +0100 Subject: [PATCH 06/13] Clearer makefile for building README --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index f90248f7..92d1c344 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,7 @@ ci: py.test -n 8 --boxed --junitxml=report.xml test-readme: - python setup.py check -r -s + @python setup.py check --restructuredtext --strict && ([ $$? -eq 0 ] && echo "README.rst and HISTORY.rst ok") || echo "Invalid markup in README.rst or HISTORY.rst!" flake8: flake8 --ignore=E501,F401,E128,E402,E731,F821 requests From d8e236780981f28228441029c689a5e8dccdd71b Mon Sep 17 00:00:00 2001 From: Nate Prewitt Date: Thu, 8 Jun 2017 12:07:45 -0600 Subject: [PATCH 07/13] convert version compatibility checks to warning --- requests/__init__.py | 52 ++++++++++++++++++++++++------------------ requests/exceptions.py | 5 ++++ 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/requests/__init__.py b/requests/__init__.py index d4461ec9..90a0d11a 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -40,34 +40,44 @@ is at . :license: Apache 2.0, see LICENSE for more details. """ -# Check urllib3 for compatibility. import urllib3 -urllib3_version = urllib3.__version__.split('.') -# Sometimes, urllib3 only reports its version as 16.1. -if len(urllib3_version) == 2: - urllib3_version.append('0') -major, minor, patch = urllib3_version -major, minor, patch = int(major), int(minor), int(patch) -# urllib3 >= 1.21.1, < 1.22 -try: +import chardet +import warnings +from .exceptions import RequestsDependencyWarning + + +def check_compatibility(urllib3_version, chardet_version): + urllib3_version = urllib3_version.split('.') + assert urllib3_version != ['dev'] # Verify urllib3 isn't installed from git. + + # Sometimes, urllib3 only reports its version as 16.1. + if len(urllib3_version) == 2: + urllib3_version.append('0') + + # Check urllib3 for compatibility. + major, minor, patch = urllib3_version # noqa: F811 + major, minor, patch = int(major), int(minor), int(patch) + # urllib3 >= 1.21.1, < 1.22 assert major == 1 assert minor >= 21 assert minor <= 22 -except AssertionError: - raise RuntimeError('Requests dependency \'urllib3\' must be version >= 1.21.1, < 1.22!') - -# Check chardet for compatibility. -import chardet -major, minor, patch = chardet.__version__.split('.')[:3] -major, minor, patch = int(major), int(minor), int(patch) -# chardet >= 3.0.2, < 3.1.0 -try: + # Check chardet for compatibility. + major, minor, patch = chardet_version.split('.')[:3] + major, minor, patch = int(major), int(minor), int(patch) + # chardet >= 3.0.2, < 3.1.0 assert major == 3 assert minor < 1 assert patch >= 2 -except AssertionError: - raise RuntimeError('Requests dependency \'chardet\' must be version >= 3.0.2, < 3.1.0!') + + +# Check imported dependencies for compatibility. +try: + check_compatibility(urllib3.__version__, chardet.__version__) +except (AssertionError, ValueError): + warnings.warn("urllib3 ({0}) or chardet ({1}) doesn't match a supported " + "version!".format(urllib3.__version__, chardet.__version__), + RequestsDependencyWarning) # Attempt to enable urllib3's SNI support, if possible try: @@ -76,8 +86,6 @@ try: except ImportError: pass -import warnings - # urllib3's DependencyWarnings should be silenced. from urllib3.exceptions import DependencyWarning warnings.simplefilter('ignore', DependencyWarning) diff --git a/requests/exceptions.py b/requests/exceptions.py index da5af10b..be7eaed6 100644 --- a/requests/exceptions.py +++ b/requests/exceptions.py @@ -115,3 +115,8 @@ class RequestsWarning(Warning): class FileModeWarning(RequestsWarning, DeprecationWarning): """A file was opened in text mode, but Requests determined its binary length.""" pass + + +class RequestsDependencyWarning(RequestsWarning): + """An imported dependency doesn't match the expected version range.""" + pass From 22d12b0501fa633b80bcda303a718696e408ebfb Mon Sep 17 00:00:00 2001 From: JiuLi Gao Date: Fri, 9 Jun 2017 16:56:14 +0800 Subject: [PATCH 08/13] typo --- requests/api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/api.py b/requests/api.py index b2ce1a96..bc2115c1 100644 --- a/requests/api.py +++ b/requests/api.py @@ -73,7 +73,7 @@ def get(url, params=None, **kwargs): def options(url, **kwargs): - r"""Sends a OPTIONS request. + r"""Sends an OPTIONS request. :param url: URL for the new :class:`Request` object. :param \*\*kwargs: Optional arguments that ``request`` takes. From 1d7fd6c8b305351ba34280492fa3a9cf968cfbb8 Mon Sep 17 00:00:00 2001 From: mlcrazy Date: Tue, 6 Jun 2017 17:27:25 -0400 Subject: [PATCH 09/13] Fixes error swallowing in set_environ --- AUTHORS.rst | 1 + requests/utils.py | 14 +++++++------- tests/test_utils.py | 30 ++++++++++++++++++++++++++++-- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 7e0bddf0..0be2effc 100755 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -183,3 +183,4 @@ Patches and Suggestions - Shmuel Amar (`@shmuelamar `_) - Gary Wu (`@garywu `_) - Ryan Pineo (`@ryanpineo `_) +- Matt Liu (`@mlcrazy `_) diff --git a/requests/utils.py b/requests/utils.py index 25af9923..1e4960d7 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -612,18 +612,18 @@ def set_environ(env_name, value): the environment variable 'env_name'. If 'value' is None, do nothing""" - if value is not None: + value_changed = value is not None + if value_changed: old_value = os.environ.get(env_name) os.environ[env_name] = value try: yield finally: - if value is None: - return - if old_value is None: - del os.environ[env_name] - else: - os.environ[env_name] = old_value + if value_changed: + if old_value is None: + del os.environ[env_name] + else: + os.environ[env_name] = old_value def should_bypass_proxies(url, no_proxy): diff --git a/tests/test_utils.py b/tests/test_utils.py index 41858b37..b3f398ee 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- import os +import copy from io import BytesIO import pytest @@ -17,7 +18,7 @@ from requests.utils import ( requote_uri, select_proxy, should_bypass_proxies, super_len, to_key_val_list, to_native_string, unquote_header_value, unquote_unreserved, - urldefragauth, add_dict_to_cookiejar) + urldefragauth, add_dict_to_cookiejar, set_environ) from requests._internal_utils import unicode_is_ascii from .compat import StringIO, cStringIO @@ -651,4 +652,29 @@ def test_should_bypass_proxies_win_registry(url, expected, override, monkeypatch.setenv('NO_PROXY', '') monkeypatch.setattr(winreg, 'OpenKey', OpenKey) monkeypatch.setattr(winreg, 'QueryValueEx', QueryValueEx) - assert should_bypass_proxies(url, no_proxy=None) == expected + + +@pytest.mark.parametrize( + 'env_name, value', ( + ('no_proxy', '192.168.0.0/24,127.0.0.1,localhost.localdomain'), + ('no_proxy', None), + ('a_new_key', '192.168.0.0/24,127.0.0.1,localhost.localdomain'), + ('a_new_key', None), + )) +def test_set_environ(env_name, value): + """Tests set_environ will set environ values and will restore the environ.""" + environ_copy = copy.deepcopy(os.environ) + with set_environ(env_name, value): + assert os.environ.get(env_name) == value + + assert os.environ == environ_copy + + +def test_set_environ_raises_exception(): + """Tests set_environ will raise exceptions in context when the + value parameter is None.""" + with pytest.raises(Exception) as exception: + with set_environ('test1', None): + raise Exception('Expected exception') + + assert 'Expected exception' in str(exception.value) From 761e39b443b615b18a3b13f1bdd92d23e7cff204 Mon Sep 17 00:00:00 2001 From: mlcrazy Date: Fri, 9 Jun 2017 14:16:32 -0400 Subject: [PATCH 10/13] Adds changelog entry for #4134 --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index edd07ae1..6582073b 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -10,6 +10,7 @@ dev - Resolve installation failure if multiprocessing is not available - Resolve tests crash if multiprocessing is not able to determine the number of CPU cores +- Resolve error swallowing in utils set_environ generator 2.17.3 (2017-05-29) @@ -1476,4 +1477,3 @@ This is not a backwards compatible change. * Frustration * Conception - From edcc894a2e08b49729b98ad91092dbaada2aa374 Mon Sep 17 00:00:00 2001 From: mlcrazy Date: Fri, 9 Jun 2017 14:29:28 -0400 Subject: [PATCH 11/13] Adds to AUTHORS --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 0be2effc..c5218c6a 100755 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -183,4 +183,5 @@ Patches and Suggestions - Shmuel Amar (`@shmuelamar `_) - Gary Wu (`@garywu `_) - Ryan Pineo (`@ryanpineo `_) +- Ed Morley (`@edmorley `_) - Matt Liu (`@mlcrazy `_) From c6a634715637e971e485766bfa37c73613ab7b82 Mon Sep 17 00:00:00 2001 From: Justin Mayhew Date: Sat, 10 Jun 2017 20:38:02 -0300 Subject: [PATCH 12/13] Remove exec permission from files that shouldn't have it --- AUTHORS.rst | 0 requests/sessions.py | 0 tests/test_lowlevel.py | 0 tests/test_requests.py | 1 - 4 files changed, 1 deletion(-) mode change 100755 => 100644 AUTHORS.rst mode change 100755 => 100644 requests/sessions.py mode change 100755 => 100644 tests/test_lowlevel.py mode change 100755 => 100644 tests/test_requests.py diff --git a/AUTHORS.rst b/AUTHORS.rst old mode 100755 new mode 100644 diff --git a/requests/sessions.py b/requests/sessions.py old mode 100755 new mode 100644 diff --git a/tests/test_lowlevel.py b/tests/test_lowlevel.py old mode 100755 new mode 100644 diff --git a/tests/test_requests.py b/tests/test_requests.py old mode 100755 new mode 100644 index 365ffcb2..194a03b3 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # -*- coding: utf-8 -*- """Tests for Requests.""" From a0f0258eeea538a12450977584f8d3dca8927ba7 Mon Sep 17 00:00:00 2001 From: Felix Yan Date: Mon, 12 Jun 2017 01:27:11 +0800 Subject: [PATCH 13/13] Fix a typo: paramters -> parameters --- tests/test_requests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_requests.py b/tests/test_requests.py index 194a03b3..cfafc6e4 100644 --- a/tests/test_requests.py +++ b/tests/test_requests.py @@ -2369,7 +2369,7 @@ class TestPreparingURLs(object): ) def test_parameters_for_nonstandard_schemes(self, input, params, expected): """ - Setting paramters for nonstandard schemes is allowed if those schemes + Setting parameters for nonstandard schemes is allowed if those schemes begin with "http", and is forbidden otherwise. """ r = requests.Request('GET', url=input, params=params)