From e7c9bbb96f8cebe873e26bf2f5f306c130e98e7d Mon Sep 17 00:00:00 2001 From: Jeff Mancuso Date: Thu, 23 May 2013 11:21:29 -0400 Subject: [PATCH 01/37] Only switch to chunked if we don't know the length of a file like object. This fixes the case of trying to upload a 0-length file - chunked upload was being forced. Services like S3 that disallow chunked upload will fail. --- requests/models.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requests/models.py b/requests/models.py index 6cf2aaa1..d894d9ff 100644 --- a/requests/models.py +++ b/requests/models.py @@ -364,7 +364,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): try: length = super_len(data) except (TypeError, AttributeError): - length = False + length = None if is_stream: body = data @@ -372,7 +372,7 @@ class PreparedRequest(RequestEncodingMixin, RequestHooksMixin): if files: raise NotImplementedError('Streamed bodies and files are mutually exclusive.') - if length: + if length is not None: self.headers['Content-Length'] = str(length) else: self.headers['Transfer-Encoding'] = 'chunked' From 3004ad539875ffc2c9ec5a376af7203189ce6fe1 Mon Sep 17 00:00:00 2001 From: Viktor Haag Date: Fri, 24 May 2013 11:26:00 -0400 Subject: [PATCH 02/37] Lower-ify url before checking against prefix with startswith() --- requests/adapters.py | 2 +- requests/sessions.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requests/adapters.py b/requests/adapters.py index 98b7317e..b4502796 100644 --- a/requests/adapters.py +++ b/requests/adapters.py @@ -118,7 +118,7 @@ class HTTPAdapter(BaseAdapter): :param verify: Whether we should actually verify the certificate. :param cert: The SSL certificate to verify. """ - if url.startswith('https') and verify: + if url.lower.startswith('https') and verify: cert_loc = None diff --git a/requests/sessions.py b/requests/sessions.py index f4aeeee6..ccd76340 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -467,7 +467,7 @@ class Session(SessionRedirectMixin): """Returns the appropriate connnection adapter for the given URL.""" for (prefix, adapter) in self.adapters.items(): - if url.startswith(prefix): + if url.lower.startswith(prefix): return adapter # Nothing matches :-/ From 5e94f38001fdaccda924aab9e5f0ffc064492129 Mon Sep 17 00:00:00 2001 From: Viktor Haag Date: Fri, 24 May 2013 14:01:30 -0400 Subject: [PATCH 03/37] - fixed func call syntax on lower to lower() - added test cases for trying to test GETS on mixed-case schemas --- requests/adapters.py | 2 +- requests/sessions.py | 2 +- test_requests.py | 21 +++++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/requests/adapters.py b/requests/adapters.py index b4502796..b4f5bf6c 100644 --- a/requests/adapters.py +++ b/requests/adapters.py @@ -118,7 +118,7 @@ class HTTPAdapter(BaseAdapter): :param verify: Whether we should actually verify the certificate. :param cert: The SSL certificate to verify. """ - if url.lower.startswith('https') and verify: + if url.lower().startswith('https') and verify: cert_loc = None diff --git a/requests/sessions.py b/requests/sessions.py index ccd76340..a55f84da 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -467,7 +467,7 @@ class Session(SessionRedirectMixin): """Returns the appropriate connnection adapter for the given URL.""" for (prefix, adapter) in self.adapters.items(): - if url.lower.startswith(prefix): + if url.lower().startswith(prefix): return adapter # Nothing matches :-/ diff --git a/test_requests.py b/test_requests.py index 4a4831e5..36006008 100755 --- a/test_requests.py +++ b/test_requests.py @@ -87,6 +87,27 @@ class RequestsTestCase(unittest.TestCase): self.assertEqual(request.url, "http://example.com/path?key=value&a=b#fragment") + def test_mixed_case_scheme_acceptable(self): + s = requests.Session() + r = requests.Request('GET', 'HTTP://httbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'hTTp://httbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'HttP://httbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'HTTPS://httbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'hTTps://httbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'HttPs://httbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + def test_HTTP_200_OK_GET_ALTERNATIVE(self): r = requests.Request('GET', httpbin('get')) s = requests.Session() From 01993d21dc7d6fa8f0acf657425d2fead9b2ddea Mon Sep 17 00:00:00 2001 From: Viktor Haag Date: Fri, 24 May 2013 16:14:14 -0400 Subject: [PATCH 04/37] added tests for mixed-case scheme URLs, changed adapters passing down URLs into urllib3 by lower-ifying them so that the underlying pool manager can effectively pool by scheme as dictionary key --- requests/adapters.py | 6 +++--- test_requests.py | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/requests/adapters.py b/requests/adapters.py index b4f5bf6c..7b4916ca 100644 --- a/requests/adapters.py +++ b/requests/adapters.py @@ -190,13 +190,13 @@ class HTTPAdapter(BaseAdapter): :param proxies: (optional) A Requests-style dictionary of proxies used on this request. """ proxies = proxies or {} - proxy = proxies.get(urlparse(url).scheme) + proxy = proxies.get(urlparse(url.lower()).scheme) if proxy: - proxy = prepend_scheme_if_needed(proxy, urlparse(url).scheme) + proxy = prepend_scheme_if_needed(proxy, urlparse(url.lower()).scheme) conn = ProxyManager(self.poolmanager.connection_from_url(proxy)) else: - conn = self.poolmanager.connection_from_url(url) + conn = self.poolmanager.connection_from_url(url.lower()) return conn diff --git a/test_requests.py b/test_requests.py index 36006008..a3463340 100755 --- a/test_requests.py +++ b/test_requests.py @@ -89,22 +89,29 @@ class RequestsTestCase(unittest.TestCase): def test_mixed_case_scheme_acceptable(self): s = requests.Session() - r = requests.Request('GET', 'HTTP://httbin.org/get') + r = requests.Request('GET', 'http://httpbin.org/get') r = s.send(r.prepare()) self.assertEqual(r.status_code,200) - r = requests.Request('GET', 'hTTp://httbin.org/get') + s = requests.Session() + r = requests.Request('GET', 'HTTP://httpbin.org/get') r = s.send(r.prepare()) self.assertEqual(r.status_code,200) - r = requests.Request('GET', 'HttP://httbin.org/get') + r = requests.Request('GET', 'hTTp://httpbin.org/get') r = s.send(r.prepare()) self.assertEqual(r.status_code,200) - r = requests.Request('GET', 'HTTPS://httbin.org/get') + r = requests.Request('GET', 'HttP://httpbin.org/get') r = s.send(r.prepare()) self.assertEqual(r.status_code,200) - r = requests.Request('GET', 'hTTps://httbin.org/get') + r = requests.Request('GET', 'https://httpbin.org/get') r = s.send(r.prepare()) self.assertEqual(r.status_code,200) - r = requests.Request('GET', 'HttPs://httbin.org/get') + r = requests.Request('GET', 'HTTPS://httpbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'hTTps://httpbin.org/get') + r = s.send(r.prepare()) + self.assertEqual(r.status_code,200) + r = requests.Request('GET', 'HttPs://httpbin.org/get') r = s.send(r.prepare()) self.assertEqual(r.status_code,200) From e715d7184b98f6c2a8c4810e7524e2e9a5a033c3 Mon Sep 17 00:00:00 2001 From: Bob Carroll Date: Mon, 29 Apr 2013 13:35:44 -0700 Subject: [PATCH 05/37] resolve_redirects no longer throws an InvalidSchema exception when the scheme is uppercase --- requests/sessions.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/requests/sessions.py b/requests/sessions.py index f4aeeee6..776f97a7 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -97,6 +97,10 @@ class SessionRedirectMixin(object): parsed_rurl = urlparse(resp.url) url = '%s:%s' % (parsed_rurl.scheme, url) + # The scheme should be lower case... + scheme, uri = url.split('://') + url = '%s://%s' % (scheme.lower(), uri) + # Facilitate non-RFC2616-compliant 'location' headers # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') # Compliant with RFC3986, we percent encode the url. From 72e155e529ae9f1892c5dffb5a021fa36ca0243e Mon Sep 17 00:00:00 2001 From: Bob Carroll Date: Tue, 30 Apr 2013 23:17:18 -0700 Subject: [PATCH 06/37] resolve_redirects now checks for a scheme before converting the scheme to lowercase, added tests or the scheme casing --- requests/sessions.py | 5 +++-- test_requests.py | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 776f97a7..af63d497 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -98,8 +98,9 @@ class SessionRedirectMixin(object): url = '%s:%s' % (parsed_rurl.scheme, url) # The scheme should be lower case... - scheme, uri = url.split('://') - url = '%s://%s' % (scheme.lower(), uri) + if '://' in url: + scheme, uri = url.split('://', 1) + url = '%s://%s' % (scheme.lower(), uri) # Facilitate non-RFC2616-compliant 'location' headers # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') diff --git a/test_requests.py b/test_requests.py index 4a4831e5..5517edac 100755 --- a/test_requests.py +++ b/test_requests.py @@ -495,6 +495,9 @@ class RequestsTestCase(unittest.TestCase): headers['ACCEPT'.encode('ascii')], 'application/json' ) + def test_uppercase_scheme(self): + r = requests.get('HTTP://example.com/') + self.assertEqual(r.status_code, 200) def test_transport_adapter_ordering(self): s = requests.Session() @@ -702,5 +705,10 @@ class TestCaseInsensitiveDict(unittest.TestCase): self.assertEqual(frozenset(cid), keyset) + + def test_uppercase_scheme_redirect(self): + r = requests.get(httpbin('redirect-to'), params={'url': 'HTTP://example.com/'}) + + if __name__ == '__main__': unittest.main() From 489a412f96c87f90d5a4788ede2d7a8a8863c91d Mon Sep 17 00:00:00 2001 From: Bob Carroll Date: Tue, 30 Apr 2013 23:31:16 -0700 Subject: [PATCH 07/37] I probably should add my name too --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 307dbb30..a75016eb 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -125,6 +125,7 @@ Patches and Suggestions - Dmitry Medvinsky - Bryce Boe @bboe - Colin Dunklau @cdunklau +- Bob Carroll @rcarz - Hugo Osvaldo Barrera @hobarrera - Łukasz Langa @llanga - Dave Shawley From f3036adc4f156627dce26a6106e22d3631940d3e Mon Sep 17 00:00:00 2001 From: Bob Carroll Date: Wed, 1 May 2013 10:44:31 -0700 Subject: [PATCH 08/37] added assertion to test_uppercase_scheme_redirect for the response code --- test_requests.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_requests.py b/test_requests.py index 5517edac..017058f5 100755 --- a/test_requests.py +++ b/test_requests.py @@ -708,7 +708,7 @@ class TestCaseInsensitiveDict(unittest.TestCase): def test_uppercase_scheme_redirect(self): r = requests.get(httpbin('redirect-to'), params={'url': 'HTTP://example.com/'}) - + self.assertEqual(r.status_code, 200) if __name__ == '__main__': unittest.main() From d59510481ad624641ec404bac423e8ab44f0f41b Mon Sep 17 00:00:00 2001 From: Bob Carroll Date: Wed, 1 May 2013 11:37:58 -0700 Subject: [PATCH 09/37] this didn't merge properly --- test_requests.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/test_requests.py b/test_requests.py index 017058f5..02a1837f 100755 --- a/test_requests.py +++ b/test_requests.py @@ -499,6 +499,9 @@ class RequestsTestCase(unittest.TestCase): r = requests.get('HTTP://example.com/') self.assertEqual(r.status_code, 200) + def test_uppercase_scheme_redirect(self): + r = requests.get(httpbin('redirect-to'), params={'url': 'HTTP://example.com/'}) + self.assertEqual(r.status_code, 200) def test_transport_adapter_ordering(self): s = requests.Session() order = ['https://', 'http://'] @@ -705,10 +708,5 @@ class TestCaseInsensitiveDict(unittest.TestCase): self.assertEqual(frozenset(cid), keyset) - - def test_uppercase_scheme_redirect(self): - r = requests.get(httpbin('redirect-to'), params={'url': 'HTTP://example.com/'}) - self.assertEqual(r.status_code, 200) - if __name__ == '__main__': unittest.main() From 2c5ea69e5df963a4914424639b2a7382cf82e4fb Mon Sep 17 00:00:00 2001 From: Bob Carroll Date: Sun, 26 May 2013 14:46:36 -0700 Subject: [PATCH 10/37] rebased with upstream/master --- test_requests.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test_requests.py b/test_requests.py index 02a1837f..07825e55 100755 --- a/test_requests.py +++ b/test_requests.py @@ -495,6 +495,7 @@ class RequestsTestCase(unittest.TestCase): headers['ACCEPT'.encode('ascii')], 'application/json' ) + def test_uppercase_scheme(self): r = requests.get('HTTP://example.com/') self.assertEqual(r.status_code, 200) @@ -502,6 +503,7 @@ class RequestsTestCase(unittest.TestCase): def test_uppercase_scheme_redirect(self): r = requests.get(httpbin('redirect-to'), params={'url': 'HTTP://example.com/'}) self.assertEqual(r.status_code, 200) + def test_transport_adapter_ordering(self): s = requests.Session() order = ['https://', 'http://'] From 340931ba8edbbfcd3bc7def1bebc989650b6d05d Mon Sep 17 00:00:00 2001 From: Julien Bouquillon Date: Wed, 29 May 2013 12:57:49 +0300 Subject: [PATCH 11/37] Update quickstart.rst rephrase misleading info about `raise_for_status` --- docs/user/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst index cb4ef579..1e365871 100644 --- a/docs/user/quickstart.rst +++ b/docs/user/quickstart.rst @@ -260,7 +260,7 @@ reference:: >>> r.status_code == requests.codes.ok True -If we made a bad request (non-200 response), we can raise it with +If we made a bad request (a 4XX client error or 5XX server error response), we can raise it with :class:`Response.raise_for_status()`:: >>> bad_r = requests.get('http://httpbin.org/status/404') From 93be6916f98f191eee9ac053994f61e99869735b Mon Sep 17 00:00:00 2001 From: James Clarke Date: Fri, 31 May 2013 18:19:34 -0700 Subject: [PATCH 12/37] Use urllib to retrieve environment proxies. This has the added benefit of including proxies defined by the OS X System Configuration framework and in the Windows registry, rather than only checking os.environ. --- requests/compat.py | 4 ++-- requests/utils.py | 28 +++++++++++++--------------- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/requests/compat.py b/requests/compat.py index bcf94b00..e5f6e1d8 100644 --- a/requests/compat.py +++ b/requests/compat.py @@ -83,7 +83,7 @@ except ImportError: # --------- if is_py2: - from urllib import quote, unquote, quote_plus, unquote_plus, urlencode + from urllib import quote, unquote, quote_plus, unquote_plus, urlencode, getproxies, proxy_bypass from urlparse import urlparse, urlunparse, urljoin, urlsplit, urldefrag from urllib2 import parse_http_list import cookielib @@ -100,7 +100,7 @@ if is_py2: elif is_py3: from urllib.parse import urlparse, urlunparse, urljoin, urlsplit, urlencode, quote, unquote, quote_plus, unquote_plus, urldefrag - from urllib.request import parse_http_list + from urllib.request import parse_http_list, getproxies, proxy_bypass from http import cookiejar as cookielib from http.cookies import Morsel from io import StringIO diff --git a/requests/utils.py b/requests/utils.py index b21bf8fb..ea87c616 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -22,6 +22,7 @@ from . import __version__ from . import certs from .compat import parse_http_list as _parse_list_header from .compat import quote, urlparse, bytes, str, OrderedDict, urlunparse +from .compat import getproxies, proxy_bypass from .cookies import RequestsCookieJar, cookiejar_from_dict from .structures import CaseInsensitiveDict @@ -386,37 +387,34 @@ def requote_uri(uri): def get_environ_proxies(url): """Return a dict of environment proxies.""" - proxy_keys = [ - 'all', - 'http', - 'https', - 'ftp', - 'socks' - ] - get_proxy = lambda k: os.environ.get(k) or os.environ.get(k.upper()) # First check whether no_proxy is defined. If it is, check that the URL # we're getting isn't in the no_proxy list. no_proxy = get_proxy('no_proxy') - + netloc = urlparse(url).netloc + if no_proxy: # We need to check whether we match here. We need to see if we match # the end of the netloc, both with and without the port. no_proxy = no_proxy.split(',') - netloc = urlparse(url).netloc - + for host in no_proxy: if netloc.endswith(host) or netloc.split(':')[0].endswith(host): # The URL does match something in no_proxy, so we don't want # to apply the proxies on this URL. return {} + + # If the system proxy settings indicate that this URL should be bypassed, + # don't proxy. + if proxy_bypass(netloc): + return {} # If we get here, we either didn't have no_proxy set or we're not going - # anywhere that no_proxy applies to. - proxies = [(key, get_proxy(key + '_proxy')) for key in proxy_keys] - return dict([(key, val) for (key, val) in proxies if val]) - + # anywhere that no_proxy applies to, and the system settings don't require + # bypassing the proxy for the current URL. + return getproxies() + def default_user_agent(): """Return a string representing the default user agent.""" From f705c41c1d55b1e023226f2803709c17e6b024bb Mon Sep 17 00:00:00 2001 From: James Clarke Date: Fri, 31 May 2013 18:19:42 -0700 Subject: [PATCH 13/37] Added myself to AUTHORS. --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 307dbb30..42a387f9 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -128,3 +128,4 @@ Patches and Suggestions - Hugo Osvaldo Barrera @hobarrera - Łukasz Langa @llanga - Dave Shawley +- James Clarke (jam) From 961790f95c7c06dc073d882acb28810a22d27b77 Mon Sep 17 00:00:00 2001 From: wasw100 Date: Thu, 6 Jun 2013 19:15:09 +0800 Subject: [PATCH 14/37] cookies.morsel_to_cookie(morsel) raise TypeError repaired. morsel_to_cookie(mosel) method raise TypeError: create_cookie() got unexpected keyword arguments: ['path_specified', 'domain_specified', 'port_specified', 'domain_initial_dot']. so we should remove these param from create_cookie(...) --- requests/cookies.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/requests/cookies.py b/requests/cookies.py index d759d0a9..6b2d4cd5 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -359,12 +359,8 @@ def morsel_to_cookie(morsel): value=morsel.value, version=morsel['version'] or 0, port=None, - port_specified=False, domain=morsel['domain'], - domain_specified=bool(morsel['domain']), - domain_initial_dot=morsel['domain'].startswith('.'), path=morsel['path'], - path_specified=bool(morsel['path']), secure=bool(morsel['secure']), expires=morsel['max-age'] or morsel['expires'], discard=False, From 767f758aacba8f70721fc1abbeb1d8ad929a084c Mon Sep 17 00:00:00 2001 From: wasw100 Date: Fri, 7 Jun 2013 00:02:45 +0800 Subject: [PATCH 15/37] cookies.morsel_to_cookie morsel['expires'] can't be strtime, and max-age convert to expires problem repair --- requests/cookies.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/requests/cookies.py b/requests/cookies.py index 6b2d4cd5..d62f0cd1 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -6,6 +6,7 @@ Compatibility code to be able to use `cookielib.CookieJar` with requests. requests.utils imports from here, so be careful with imports. """ +import time import collections from .compat import cookielib, urlparse, Morsel @@ -354,6 +355,14 @@ def create_cookie(name, value, **kwargs): def morsel_to_cookie(morsel): """Convert a Morsel object into a Cookie containing the one k/v pair.""" + expires = None + if morsel["max-age"]: + expires = time.time() + morsel["max-age"] + elif morsel['expires']: + expires = morsel['expires'] + if type(expires) == type(""): + time_template = "%a, %d-%b-%Y %H:%M:%S GMT" + expires = time.mktime(time.strptime(expires, time_template)) c = create_cookie( name=morsel.key, value=morsel.value, @@ -362,7 +371,7 @@ def morsel_to_cookie(morsel): domain=morsel['domain'], path=morsel['path'], secure=bool(morsel['secure']), - expires=morsel['max-age'] or morsel['expires'], + expires=expires, discard=False, comment=morsel['comment'], comment_url=bool(morsel['comment']), From 2ed976ea7147a9d0c18998e02b16d691b6798a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Sat, 8 Jun 2013 08:22:15 +0000 Subject: [PATCH 16/37] update urllib3 to 60ba176f5d --- requests/packages/urllib3/connectionpool.py | 3 +- requests/packages/urllib3/contrib/ntlmpool.py | 2 +- .../packages/urllib3/contrib/pyopenssl.py | 3 ++ requests/packages/urllib3/filepost.py | 2 +- requests/packages/urllib3/poolmanager.py | 9 +++++ requests/packages/urllib3/request.py | 2 +- requests/packages/urllib3/response.py | 37 ++++++++++++++++++- 7 files changed, 52 insertions(+), 6 deletions(-) diff --git a/requests/packages/urllib3/connectionpool.py b/requests/packages/urllib3/connectionpool.py index f3e92608..3d7d166a 100644 --- a/requests/packages/urllib3/connectionpool.py +++ b/requests/packages/urllib3/connectionpool.py @@ -110,7 +110,7 @@ class VerifiedHTTPSConnection(HTTPSConnection): if self.assert_fingerprint: assert_fingerprint(self.sock.getpeercert(binary_form=True), self.assert_fingerprint) - else: + elif self.assert_hostname is not False: match_hostname(self.sock.getpeercert(), self.assert_hostname or self.host) @@ -513,6 +513,7 @@ class HTTPSConnectionPool(HTTPConnectionPool): :class:`.VerifiedHTTPSConnection` uses one of ``assert_fingerprint``, ``assert_hostname`` and ``host`` in this order to verify connections. + If ``assert_hostname`` is False, no verification is done. The ``key_file``, ``cert_file``, ``cert_reqs``, ``ca_certs`` and ``ssl_version`` are only used if :mod:`ssl` is available and are fed into diff --git a/requests/packages/urllib3/contrib/ntlmpool.py b/requests/packages/urllib3/contrib/ntlmpool.py index 277ee0b2..b8cd9330 100644 --- a/requests/packages/urllib3/contrib/ntlmpool.py +++ b/requests/packages/urllib3/contrib/ntlmpool.py @@ -33,7 +33,7 @@ class NTLMConnectionPool(HTTPSConnectionPool): def __init__(self, user, pw, authurl, *args, **kwargs): """ authurl is a random URL on the server that is protected by NTLM. - user is the Windows user, probably in the DOMAIN\username format. + user is the Windows user, probably in the DOMAIN\\username format. pw is the password for the user. """ super(NTLMConnectionPool, self).__init__(*args, **kwargs) diff --git a/requests/packages/urllib3/contrib/pyopenssl.py b/requests/packages/urllib3/contrib/pyopenssl.py index 5c4c6d8d..9829e80b 100644 --- a/requests/packages/urllib3/contrib/pyopenssl.py +++ b/requests/packages/urllib3/contrib/pyopenssl.py @@ -115,6 +115,9 @@ class WrappedSocket(object): def sendall(self, data): return self.connection.sendall(data) + def close(self): + return self.connection.shutdown() + def getpeercert(self, binary_form=False): x509 = self.connection.get_peer_certificate() if not x509: diff --git a/requests/packages/urllib3/filepost.py b/requests/packages/urllib3/filepost.py index 470309a0..526a7409 100644 --- a/requests/packages/urllib3/filepost.py +++ b/requests/packages/urllib3/filepost.py @@ -1,5 +1,5 @@ # urllib3/filepost.py -# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt) +# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) # # This module is part of urllib3 and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php diff --git a/requests/packages/urllib3/poolmanager.py b/requests/packages/urllib3/poolmanager.py index ce0c248e..2a1aa48b 100644 --- a/requests/packages/urllib3/poolmanager.py +++ b/requests/packages/urllib3/poolmanager.py @@ -6,6 +6,11 @@ import logging +try: # Python 3 + from urllib.parse import urljoin +except ImportError: + from urlparse import urljoin + from ._collections import RecentlyUsedContainer from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool from .connectionpool import connection_from_url, port_by_scheme @@ -145,6 +150,10 @@ class PoolManager(RequestMethods): if not redirect_location: return response + # Support relative URLs for redirecting. + redirect_location = urljoin(url, redirect_location) + + # RFC 2616, Section 10.3.4 if response.status == 303: method = 'GET' diff --git a/requests/packages/urllib3/request.py b/requests/packages/urllib3/request.py index bf0256e9..66a9a0e6 100644 --- a/requests/packages/urllib3/request.py +++ b/requests/packages/urllib3/request.py @@ -30,7 +30,7 @@ class RequestMethods(object): in the URL (such as GET, HEAD, DELETE). :meth:`.request_encode_body` is for sending requests whose fields are - encoded in the *body* of the request using multipart or www-orm-urlencoded + encoded in the *body* of the request using multipart or www-form-urlencoded (such as for POST, PUT, PATCH). :meth:`.request` is for making any kind of request, it will look up the diff --git a/requests/packages/urllib3/response.py b/requests/packages/urllib3/response.py index 2fa40788..05bc38a3 100644 --- a/requests/packages/urllib3/response.py +++ b/requests/packages/urllib3/response.py @@ -1,5 +1,5 @@ # urllib3/response.py -# Copyright 2008-2012 Andrey Petrov and contributors (see CONTRIBUTORS.txt) +# Copyright 2008-2013 Andrey Petrov and contributors (see CONTRIBUTORS.txt) # # This module is part of urllib3 and is released under # the MIT License: http://www.opensource.org/licenses/mit-license.php @@ -7,6 +7,7 @@ import logging import zlib +import io from .exceptions import DecodeError from .packages.six import string_types as basestring, binary_type @@ -48,7 +49,7 @@ def _get_decoder(mode): return DeflateDecoder() -class HTTPResponse(object): +class HTTPResponse(io.IOBase): """ HTTP Response container. @@ -239,3 +240,35 @@ class HTTPResponse(object): def getheader(self, name, default=None): return self.headers.get(name, default) + + # Overrides from io.IOBase + def close(self): + if not self.closed: + self._fp.close() + + @property + def closed(self): + if self._fp is None: + return True + elif hasattr(self._fp, 'closed'): + return self._fp.closed + elif hasattr(self._fp, 'isclosed'): # Python 2 + return self._fp.isclosed() + else: + return True + + def fileno(self): + if self._fp is None: + raise IOError("HTTPResponse has no file to get a fileno from") + elif hasattr(self._fp, "fileno"): + return self._fp.fileno() + else: + raise IOError("The file-like object this HTTPResponse is wrapped " + "around has no file descriptor") + + def flush(self): + if self._fp is not None and hasattr(self._fp, 'flush'): + return self._fp.flush() + + def readable(self): + return True From f53aed16111903d4f1fcce204e4e7a6f1fc09ee9 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Sat, 8 Jun 2013 11:09:39 +0100 Subject: [PATCH 17/37] Document blocking calls. --- docs/user/advanced.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst index a69a1404..ffcb777f 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -268,6 +268,8 @@ Then, we can make a request using our Pizza Auth:: >>> requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth')) +.. _streaming-requests + Streaming Requests ------------------ @@ -574,3 +576,19 @@ a good start would be to subclass the ``requests.adapters.BaseAdapter`` class. .. _`described here`: http://kennethreitz.org/exposures/the-future-of-python-http .. _`urllib3`: https://github.com/shazow/urllib3 +Blocking Or Non-Blocking? +------------------------- + +With the default Transport Adapter in place, Requests does not provide any kind +of non-blocking IO. The ``Response.content`` property will block until the +entire response has been downloaded. If you require more granularity, the +streaming features of the library (see :ref:`streaming-requests`) allow you to +retrieve smaller quantities of the response at a time. However, these calls +will still block. + +If you are concerned about the use of blocking IO, there are lots of projects +out there that combine Requests with one of Python's asynchronicity frameworks. +Two excellent examples are `grequests`_ and `requests-futures`_. + +.. _`grequests`: https://github.com/kennethreitz/grequests +.. _`requests-futures`: https://github.com/ross/requests-futures From 73425fbffbee293faa6cf4b2fc8ad1e6bce1b45b Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Sat, 8 Jun 2013 11:10:07 +0100 Subject: [PATCH 18/37] Remove development reference from docs sidebar. --- docs/_templates/sidebarintro.html | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index 9d07a5ca..d2ffff0d 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -11,8 +11,7 @@

Requests is an elegant and simple HTTP library for Python, built for - human beings. You are currently looking at the documentation of the - development release. + human beings.

From 798a1ffdec682fe8b9e0c3386f445ab6e1fd801c Mon Sep 17 00:00:00 2001 From: Roman Haritonov Date: Fri, 19 Apr 2013 10:12:34 +0400 Subject: [PATCH 19/37] new failing test_requests_in_history_are_not_overridden() --- test_requests.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test_requests.py b/test_requests.py index d0d8d9e6..fed70d8d 100755 --- a/test_requests.py +++ b/test_requests.py @@ -169,6 +169,7 @@ class RequestsTestCase(unittest.TestCase): } ) assert 'foo' not in s.cookies +<<<<<<< HEAD def test_request_cookie_overrides_session_cookie(self): s = requests.session() @@ -188,7 +189,13 @@ class RequestsTestCase(unittest.TestCase): assert r.json()['cookies']['foo'] == 'bar' # Make sure the session cj is still the custom one assert s.cookies is cj - + + def test_requests_in_history_are_not_overridden(self): + resp = requests.get(httpbin('redirect/3')) + urls = [r.url for r in resp.history] + req_urls = [r.request.url for r in resp.history] + self.assertEquals(urls, req_urls) + def test_user_agent_transfers(self): heads = { From 716b627c1e7c86af722affe973a11df4d5812a1b Mon Sep 17 00:00:00 2001 From: Roman Haritonov Date: Fri, 19 Apr 2013 10:13:36 +0400 Subject: [PATCH 20/37] Don't reuse PreparedRequest on redirects --- requests/sessions.py | 16 +++++++--------- test_requests.py | 1 - 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 634637f4..6d1000dc 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -71,15 +71,13 @@ class SessionRedirectMixin(object): """Receives a Response. Returns a generator of Responses.""" i = 0 - prepared_request = PreparedRequest() - prepared_request.body = req.body - prepared_request.headers = req.headers.copy() - prepared_request.hooks = req.hooks - prepared_request.method = req.method - prepared_request.url = req.url # ((resp.status_code is codes.see_other)) while (('location' in resp.headers and resp.status_code in REDIRECT_STATI)): + prepared_request = PreparedRequest() + prepared_request.body = req.body + prepared_request.headers = req.headers.copy() + prepared_request.hooks = req.hooks resp.content # Consume socket so it can be released @@ -90,7 +88,7 @@ class SessionRedirectMixin(object): resp.close() url = resp.headers['location'] - method = prepared_request.method + method = req.method # Handle redirection without scheme (see: RFC 1808 Section 4) if url.startswith('//'): @@ -114,12 +112,12 @@ class SessionRedirectMixin(object): # http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4 if (resp.status_code == codes.see_other and - prepared_request.method != 'HEAD'): + method != 'HEAD'): method = 'GET' # Do what the browsers do, despite standards... if (resp.status_code in (codes.moved, codes.found) and - prepared_request.method not in ('GET', 'HEAD')): + method not in ('GET', 'HEAD')): method = 'GET' prepared_request.method = method diff --git a/test_requests.py b/test_requests.py index fed70d8d..7ad10ee9 100755 --- a/test_requests.py +++ b/test_requests.py @@ -169,7 +169,6 @@ class RequestsTestCase(unittest.TestCase): } ) assert 'foo' not in s.cookies -<<<<<<< HEAD def test_request_cookie_overrides_session_cookie(self): s = requests.session() From 43a64d9515169c1f8ccb231749aba931fa7a5be8 Mon Sep 17 00:00:00 2001 From: Josh Schneier Date: Sun, 9 Jun 2013 11:14:05 -0400 Subject: [PATCH 21/37] fix doc typo --- docs/user/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst index ffcb777f..9e0865a9 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -49,7 +49,7 @@ Request and Response Objects ---------------------------- Whenever a call is made to requests.*() you are doing two major things. First, -you are constructing a ``Request`` object which will be sent of to a server +you are constructing a ``Request`` object which will be sent off to a server to request or query some resource. Second, a ``Response`` object is generated once ``requests`` gets a response back from the server. The response object contains all of the information returned by the server and also contains the From a6415cf8956cce8a07b8821b9a4dbb703cd1750c Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Wed, 12 Jun 2013 15:53:09 -0700 Subject: [PATCH 22/37] Link to the actual exception references Sphinx has a neat cross-referencing feature where if you include the tilde character in front of a :py: class, it'll link to the full object but provide only the last part of class name in the text. For more info see http://sphinx-doc.org/domains.html#cross-referencing-syntax --- docs/user/quickstart.rst | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/user/quickstart.rst b/docs/user/quickstart.rst index 1e365871..59d75ccb 100644 --- a/docs/user/quickstart.rst +++ b/docs/user/quickstart.rst @@ -399,15 +399,16 @@ Errors and Exceptions --------------------- In the event of a network problem (e.g. DNS failure, refused connection, etc), -Requests will raise a :class:`ConnectionError` exception. +Requests will raise a :class:`~requests.exceptions.ConnectionError` exception. -In the event of the rare invalid HTTP response, Requests will raise -an :class:`HTTPError` exception. +In the event of the rare invalid HTTP response, Requests will raise an +:class:`~requests.exceptions.HTTPError` exception. -If a request times out, a :class:`Timeout` exception is raised. +If a request times out, a :class:`~requests.exceptions.Timeout` exception is +raised. If a request exceeds the configured number of maximum redirections, a -:class:`TooManyRedirects` exception is raised. +:class:`~requests.exceptions.TooManyRedirects` exception is raised. All exceptions that Requests explicitly raises inherit from :class:`requests.exceptions.RequestException`. From ba0db9db862916e199cd8014a20ee0f95d6046fc Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Wed, 12 Jun 2013 15:56:10 -0700 Subject: [PATCH 23/37] Add self to AUTHORS --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 6ad5d4c6..96a3d49f 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -130,3 +130,4 @@ Patches and Suggestions - Łukasz Langa @llanga - Dave Shawley - James Clarke (jam) +- Kevin Burke From ecf57cac5ce1d9e365b52ded97e59038287c6a9d Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Tue, 18 Jun 2013 17:47:23 +0100 Subject: [PATCH 24/37] Update urllib3 to cffbd6b317 --- requests/packages/urllib3/response.py | 24 ++++++++++++++++++++++++ requests/packages/urllib3/util.py | 15 ++++++++++++++- 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/requests/packages/urllib3/response.py b/requests/packages/urllib3/response.py index 05bc38a3..74a5156c 100644 --- a/requests/packages/urllib3/response.py +++ b/requests/packages/urllib3/response.py @@ -11,6 +11,7 @@ import io from .exceptions import DecodeError from .packages.six import string_types as basestring, binary_type +from .util import is_fp_closed log = logging.getLogger(__name__) @@ -201,6 +202,29 @@ class HTTPResponse(io.IOBase): if self._original_response and self._original_response.isclosed(): self.release_conn() + def stream(self, amt=2**16, decode_content=None): + """ + A generator wrapper for the read() method. A call will block until + ``amt`` bytes have been read from the connection or until the + connection is closed. + + :param amt: + How much of the content to read. The generator will return up to + much data per iteration, but may return less. This is particularly + likely when using compressed data. However, the empty string will + never be returned. + + :param decode_content: + If True, will attempt to decode the body based on the + 'content-encoding' header. + """ + while not is_fp_closed(self._fp): + data = self.read(amt=amt, decode_content=decode_content) + + if data: + yield data + + @classmethod def from_httplib(ResponseCls, r, **response_kw): """ diff --git a/requests/packages/urllib3/util.py b/requests/packages/urllib3/util.py index 544f9ed9..f4eb5e94 100644 --- a/requests/packages/urllib3/util.py +++ b/requests/packages/urllib3/util.py @@ -31,7 +31,6 @@ try: # Test for SSL features except ImportError: pass - from .packages import six from .exceptions import LocationParseError, SSLError @@ -341,6 +340,20 @@ def assert_fingerprint(cert, fingerprint): .format(hexlify(fingerprint_bytes), hexlify(cert_digest))) +def is_fp_closed(obj): + """ + Checks whether a given file-like object is closed. + + :param obj: + The file-like object to check. + """ + if hasattr(obj, 'fp'): + # Object is a container for another file-like object that gets released + # on exhaustion (e.g. HTTPResponse) + return obj.fp is None + + return obj.closed + if SSLContext is not None: # Python 3.2+ def ssl_wrap_socket(sock, keyfile=None, certfile=None, cert_reqs=None, From 1faa76a86fe170fcab8ddfeb0d069ca10793f8a3 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Tue, 18 Jun 2013 17:53:28 +0100 Subject: [PATCH 25/37] Use the new urllib3 stream generator. --- requests/models.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/requests/models.py b/requests/models.py index d894d9ff..6600a91a 100644 --- a/requests/models.py +++ b/requests/models.py @@ -537,11 +537,18 @@ class Response(object): return iter_slices(self._content, chunk_size) def generate(): - while 1: - chunk = self.raw.read(chunk_size, decode_content=True) - if not chunk: - break - yield chunk + try: + # Special case for urllib3. + for chunk in self.raw.stream(chunk_size, decode_content=True): + yield chunk + except AttributeError: + # Standard file-like object. + while 1: + chunk = self.raw.read(chunk_size, decode_content=True) + if not chunk: + break + yield chunk + self._content_consumed = True gen = generate() From d57a5efab342c86e04ddb4608f489e50443cab57 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Sat, 22 Jun 2013 15:02:18 +0100 Subject: [PATCH 26/37] Packaging warning, in via @andrewgross. --- docs/dev/todo.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/dev/todo.rst b/docs/dev/todo.rst index 38378293..794c3fea 100644 --- a/docs/dev/todo.rst +++ b/docs/dev/todo.rst @@ -53,3 +53,8 @@ Are you crazy? -------------- - SPDY support would be awesome. No C extensions. + +Downstream Repackaging +-------------------- + +If you are repackaging Requests, please note that you must also redistribute the ``cacerts.pem`` file in order to get correct SSL functionality. From 90837359632b43aba7900b65b42744715f0b739a Mon Sep 17 00:00:00 2001 From: Chen Huang Date: Tue, 25 Jun 2013 18:38:59 -0400 Subject: [PATCH 27/37] @1434 Fix https://github.com/kennethreitz/requests/issues/1434 --- requests/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/utils.py b/requests/utils.py index ea87c616..37aa19e4 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -302,7 +302,7 @@ def stream_decode_response_unicode(iterator, r): rv = decoder.decode(chunk) if rv: yield rv - rv = decoder.decode('', final=True) + rv = decoder.decode(b'', final=True) if rv: yield rv From 805abee9b4da8837eab1165eba02b5db889e1fb3 Mon Sep 17 00:00:00 2001 From: Lukasz Balcerzak Date: Thu, 27 Jun 2013 13:37:39 +0200 Subject: [PATCH 28/37] Fixed wrong method call at streaming example 405 is returned if POST request is performed to http://httpbin.org/stream/20 --- docs/user/advanced.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/user/advanced.rst b/docs/user/advanced.rst index 9e0865a9..246313fc 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -281,7 +281,7 @@ To use the Twitter Streaming API to track the keyword "requests":: import json import requests - r = requests.post('http://httpbin.org/stream/20', stream=True) + r = requests.get('http://httpbin.org/stream/20', stream=True) for line in r.iter_lines(): From 188e7609b36eb2100abe3a6a5c3bbebcf1205cfb Mon Sep 17 00:00:00 2001 From: Vikram Oberoi Date: Thu, 27 Jun 2013 16:43:40 -0400 Subject: [PATCH 29/37] .netrc settings shouldn't blow away explicit auth settings on a session --- requests/sessions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requests/sessions.py b/requests/sessions.py index 6d1000dc..c24ed5aa 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -289,8 +289,8 @@ class Session(SessionRedirectMixin): for (k, v) in env_proxies.items(): proxies.setdefault(k, v) - # Set environment's basic authentication. - if not auth: + # Set environment's basic authentication if not explicitly set. + if not auth and not self.auth: auth = get_netrc_auth(url) # Look for configuration. From d9c49ad30d1107591f3b5150da6f95ec90c63784 Mon Sep 17 00:00:00 2001 From: Vikram Oberoi Date: Thu, 27 Jun 2013 17:16:42 -0400 Subject: [PATCH 30/37] Add test to verify .netrc authentication behavior. Here's what should happen: - If no credentials are given, use netrc if there's a netrc entry. - If credentials are given, they should override netrc. --- test_requests.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/test_requests.py b/test_requests.py index 7ad10ee9..07582105 100755 --- a/test_requests.py +++ b/test_requests.py @@ -234,6 +234,34 @@ class RequestsTestCase(unittest.TestCase): r = s.get(url) self.assertEqual(r.status_code, 200) + def test_basicauth_with_netrc(self): + auth = ('user', 'pass') + wrong_auth = ('wronguser', 'wrongpass') + url = httpbin('basic-auth', 'user', 'pass') + + def get_netrc_auth_mock(url): + return auth + requests.sessions.get_netrc_auth = get_netrc_auth_mock + + # Should use netrc and work. + r = requests.get(url) + self.assertEqual(r.status_code, 200) + + # Given auth should override and fail. + r = requests.get(url, auth=wrong_auth) + self.assertEqual(r.status_code, 401) + + s = requests.session() + + # Should use netrc and work. + r = s.get(url) + self.assertEqual(r.status_code, 200) + + # Given auth should override and fail. + s.auth = wrong_auth + r = s.get(url) + self.assertEqual(r.status_code, 401) + def test_DIGEST_HTTP_200_OK_GET(self): auth = HTTPDigestAuth('user', 'pass') From cdab4fabf448f287f6f537265aa23ac06a080599 Mon Sep 17 00:00:00 2001 From: Flavio Curella Date: Mon, 1 Jul 2013 13:49:43 -0500 Subject: [PATCH 31/37] unquote double-quotes cookie values --- requests/cookies.py | 5 +++++ test_requests.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/requests/cookies.py b/requests/cookies.py index d62f0cd1..a12feb56 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -259,6 +259,11 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): """Deletes a cookie given a name. Wraps cookielib.CookieJar's remove_cookie_by_name().""" remove_cookie_by_name(self, name) + def set_cookie(self, cookie, *args, **kwargs): + if cookie.value.startswith('"') and cookie.value.endswith('"'): + cookie.value = cookie.value.strip('\\"') + return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) + def update(self, other): """Updates this jar with cookies from another CookieJar or dict-like""" if isinstance(other, cookielib.CookieJar): diff --git a/test_requests.py b/test_requests.py index 7ad10ee9..b0e99c77 100755 --- a/test_requests.py +++ b/test_requests.py @@ -170,6 +170,11 @@ class RequestsTestCase(unittest.TestCase): ) assert 'foo' not in s.cookies + def test_cookie_quote_wrapped(self): + s = requests.session() + s.get(httpbin('cookies/set?foo="bar:baz"')) + self.assertTrue(s.cookies['foo'] == 'bar:baz') + def test_request_cookie_overrides_session_cookie(self): s = requests.session() s.cookies['foo'] = 'bar' From a8cf5b85027e523c54d3d496ee5467eac90555bd Mon Sep 17 00:00:00 2001 From: Flavio Curella Date: Mon, 1 Jul 2013 15:48:48 -0500 Subject: [PATCH 32/37] keep the double quotes, but don't escape them --- requests/cookies.py | 2 +- test_requests.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/requests/cookies.py b/requests/cookies.py index a12feb56..d1e5d75b 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -261,7 +261,7 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): def set_cookie(self, cookie, *args, **kwargs): if cookie.value.startswith('"') and cookie.value.endswith('"'): - cookie.value = cookie.value.strip('\\"') + cookie.value = cookie.value.replace('\\"', '') return super(RequestsCookieJar, self).set_cookie(cookie, *args, **kwargs) def update(self, other): diff --git a/test_requests.py b/test_requests.py index b0e99c77..e22b6ae3 100755 --- a/test_requests.py +++ b/test_requests.py @@ -173,7 +173,7 @@ class RequestsTestCase(unittest.TestCase): def test_cookie_quote_wrapped(self): s = requests.session() s.get(httpbin('cookies/set?foo="bar:baz"')) - self.assertTrue(s.cookies['foo'] == 'bar:baz') + self.assertTrue(s.cookies['foo'] == '"bar:baz"') def test_request_cookie_overrides_session_cookie(self): s = requests.session() From 7da16c584ae6ab9c87d4aeea857278ee886760f0 Mon Sep 17 00:00:00 2001 From: Flavio Curella Date: Mon, 1 Jul 2013 15:54:59 -0500 Subject: [PATCH 33/37] Added myself to AUTHORS --- AUTHORS.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.rst b/AUTHORS.rst index 96a3d49f..6267ddf0 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -131,3 +131,4 @@ Patches and Suggestions - Dave Shawley - James Clarke (jam) - Kevin Burke +- Flavio Curella From 555472bf1e3ab4ad6c2057c93e195427bda5a18b Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Thu, 4 Jul 2013 10:34:43 +0100 Subject: [PATCH 34/37] Remove urllib3-specific kwargs from general code --- requests/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/models.py b/requests/models.py index 6600a91a..3672b377 100644 --- a/requests/models.py +++ b/requests/models.py @@ -544,7 +544,7 @@ class Response(object): except AttributeError: # Standard file-like object. while 1: - chunk = self.raw.read(chunk_size, decode_content=True) + chunk = self.raw.read(chunk_size) if not chunk: break yield chunk From 41517e2111ce964db9f3a102b89eea103f2dc411 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Sat, 6 Jul 2013 14:04:50 +1000 Subject: [PATCH 35/37] Delete invokefile.py --- invokefile.py | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 invokefile.py diff --git a/invokefile.py b/invokefile.py deleted file mode 100644 index 2c97ed55..00000000 --- a/invokefile.py +++ /dev/null @@ -1,5 +0,0 @@ -from invoke import run, task - -@task -def build(): - print("Building!") \ No newline at end of file From 7bdf37bc272da9ef92a8cf4d58ac886f4c6b2da1 Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Sat, 13 Jul 2013 09:55:50 +0200 Subject: [PATCH 36/37] Changed the "im_used" informational status code for the value given by IANA (226) See RFC 3229 at http://tools.ietf.org/html/rfc3229#section-10.4.1 and HTTP status codes at http://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml --- requests/status_codes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/status_codes.py b/requests/status_codes.py index de384865..8992a372 100644 --- a/requests/status_codes.py +++ b/requests/status_codes.py @@ -18,7 +18,7 @@ _codes = { 205: ('reset_content', 'reset'), 206: ('partial_content', 'partial'), 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'), - 208: ('im_used',), + 226: ('im_used',), # Redirection. 300: ('multiple_choices',), From 18a736fbe67f5dfd03f95cc9e0d16cda54c9ea4d Mon Sep 17 00:00:00 2001 From: Philippe Ndiaye Date: Sat, 13 Jul 2013 11:08:01 +0200 Subject: [PATCH 37/37] Set 208 status_code to "already_reported" --- requests/status_codes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/requests/status_codes.py b/requests/status_codes.py index 8992a372..ed7a8660 100644 --- a/requests/status_codes.py +++ b/requests/status_codes.py @@ -18,6 +18,7 @@ _codes = { 205: ('reset_content', 'reset'), 206: ('partial_content', 'partial'), 207: ('multi_status', 'multiple_status', 'multi_stati', 'multiple_stati'), + 208: ('already_reported',), 226: ('im_used',), # Redirection.