From 87abd9c6094f8b682f7457181adbe768d003fd23 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Sun, 12 Jan 2014 14:27:45 -0600 Subject: [PATCH 0001/1803] Use calendar.timegm when calculating cookie expiration Fixes #1859 Credit: @lukasa --- requests/cookies.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/requests/cookies.py b/requests/cookies.py index 831c49c6d2..ea72f75eac 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -7,6 +7,7 @@ """ import time +import calendar import collections from .compat import cookielib, urlparse, urlunparse, Morsel @@ -393,8 +394,8 @@ def morsel_to_cookie(morsel): expires = time.time() + morsel['max-age'] elif morsel['expires']: time_template = '%a, %d-%b-%Y %H:%M:%S GMT' - expires = time.mktime( - time.strptime(morsel['expires'], time_template)) - time.timezone + expires = calendar.timegm(time.strptime(morsel['expires'], + time_template)) return create_cookie( comment=morsel['comment'], comment_url=bool(morsel['comment']), From 087a27aba97d3eac017d321e37bf1970f8833c1a Mon Sep 17 00:00:00 2001 From: ContinuousFunction Date: Sat, 15 Nov 2014 16:58:25 -0800 Subject: [PATCH 0002/1803] Partially addresses Issue #1572 Addresses the LocationParseError but not the DecodeError from kennethreitz#1572. When running test_requests.py, I got an error in test_session_pickling which resulted in a TypeError. I'm not sure of the reason for the TypeError but I have commented out that test. --- requests/models.py | 7 +++++-- test_requests.py | 23 ++++++++++++++--------- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/requests/models.py b/requests/models.py index 2370b67f68..b95b5bebde 100644 --- a/requests/models.py +++ b/requests/models.py @@ -20,7 +20,7 @@ from .packages.urllib3.filepost import encode_multipart_formdata from .packages.urllib3.util import parse_url from .packages.urllib3.exceptions import ( - DecodeError, ReadTimeoutError, ProtocolError) + DecodeError, ReadTimeoutError, ProtocolError, LocationParseError) from .exceptions import ( HTTPError, RequestException, MissingSchema, InvalidURL, ChunkedEncodingError, ContentDecodingError, ConnectionError, @@ -351,7 +351,10 @@ def prepare_url(self, url, params): return # Support for unicode domain names and paths. - scheme, auth, host, port, path, query, fragment = parse_url(url) + try: + scheme, auth, host, port, path, query, fragment = parse_url(url) + except LocationParseError as e: + raise ConnectionError(e.message) if not scheme: raise MissingSchema("Invalid URL {0!r}: No schema supplied. " diff --git a/test_requests.py b/test_requests.py index 4a05cb2e7c..6e49f0270c 100755 --- a/test_requests.py +++ b/test_requests.py @@ -309,6 +309,11 @@ def test_connection_error(self): with pytest.raises(ConnectionError): requests.get("http://httpbin.org:1") + def test_LocationParseError(self): + """Inputing a URL that cannot be parsed should raise a ConnectionError""" + with pytest.raises(ConnectionError): + requests.get("http://fe80::5054:ff:fe5a:fc0") + def test_basicauth_with_netrc(self): auth = ('user', 'pass') wrong_auth = ('wronguser', 'wrongpass') @@ -820,15 +825,15 @@ def test_http_error(self): assert str(error) == 'message' assert error.response == response - def test_session_pickling(self): - r = requests.Request('GET', httpbin('get')) - s = requests.Session() - - s = pickle.loads(pickle.dumps(s)) - s.proxies = getproxies() - - r = s.send(r.prepare()) - assert r.status_code == 200 +## def test_session_pickling(self): +## r = requests.Request('GET', httpbin('get')) +## s = requests.Session() +## +## s = pickle.loads(pickle.dumps(s)) +## s.proxies = getproxies() +## +## r = s.send(r.prepare()) +## assert r.status_code == 200 def test_fixes_1329(self): """ From 3246b1fe172ca3d4f098e85467234ded2f833b31 Mon Sep 17 00:00:00 2001 From: ContinuousFunction Date: Sun, 16 Nov 2014 16:39:08 -0800 Subject: [PATCH 0003/1803] Changed ConnectionError to InvalidURL --- requests/models.py | 2 +- test_requests.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/requests/models.py b/requests/models.py index b95b5bebde..8a71e28bab 100644 --- a/requests/models.py +++ b/requests/models.py @@ -354,7 +354,7 @@ def prepare_url(self, url, params): try: scheme, auth, host, port, path, query, fragment = parse_url(url) except LocationParseError as e: - raise ConnectionError(e.message) + raise InvalidURL(e.message) if not scheme: raise MissingSchema("Invalid URL {0!r}: No schema supplied. " diff --git a/test_requests.py b/test_requests.py index 6e49f0270c..d176ef45ad 100755 --- a/test_requests.py +++ b/test_requests.py @@ -310,8 +310,8 @@ def test_connection_error(self): requests.get("http://httpbin.org:1") def test_LocationParseError(self): - """Inputing a URL that cannot be parsed should raise a ConnectionError""" - with pytest.raises(ConnectionError): + """Inputing a URL that cannot be parsed should raise an InvalidURL error""" + with pytest.raises(InvalidURL): requests.get("http://fe80::5054:ff:fe5a:fc0") def test_basicauth_with_netrc(self): From cf82d4406be55c71983d05d7f6e18917540b5c69 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Mon, 1 Dec 2014 16:21:41 -0600 Subject: [PATCH 0004/1803] Update tests to work offline --- test_requests.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/test_requests.py b/test_requests.py index 4624f095ad..2d3ee628c5 100755 --- a/test_requests.py +++ b/test_requests.py @@ -258,7 +258,7 @@ def test_headers_on_session_with_None_are_not_sent(self): """Do not send headers in Session.headers with None values.""" ses = requests.Session() ses.headers['Accept-Encoding'] = None - req = requests.Request('GET', 'http://httpbin.org/get') + req = requests.Request('GET', httpbin('get')) prep = ses.prepare_request(req) assert 'Accept-Encoding' not in prep.headers @@ -1008,12 +1008,12 @@ def test_basic_auth_str_is_always_native(self): assert s == "Basic dGVzdDp0ZXN0" def test_requests_history_is_saved(self): - r = requests.get('https://httpbin.org/redirect/5') + r = requests.get(httpbin('redirect/5')) total = r.history[-1].history i = 0 for item in r.history: assert item.history == total[0:i] - i=i+1 + i = i + 1 def test_json_param_post_content_type_works(self): r = requests.post( @@ -1350,7 +1350,7 @@ def test_max_age_invalid_str(self): class TestTimeout: def test_stream_timeout(self): try: - requests.get('https://httpbin.org/delay/10', timeout=2.0) + requests.get(httpbin('delay/10'), timeout=2.0) except requests.exceptions.Timeout as e: assert 'Read timed out' in e.args[0].args[0] @@ -1450,7 +1450,7 @@ class TestRedirects: def test_requests_are_updated_each_time(self): session = RedirectSession([303, 307]) - prep = requests.Request('POST', 'http://httpbin.org/post').prepare() + prep = requests.Request('POST', httpbin('post')).prepare() r0 = session.send(prep) assert r0.request.method == 'POST' assert session.calls[-1] == SendCall((r0.request,), {}) @@ -1534,12 +1534,12 @@ def test_prepare_unicode_url(): def test_urllib3_retries(): from requests.packages.urllib3.util import Retry s = requests.Session() - s.mount('https://', HTTPAdapter(max_retries=Retry( + s.mount('http://', HTTPAdapter(max_retries=Retry( total=2, status_forcelist=[500] ))) with pytest.raises(RetryError): - s.get('https://httpbin.org/status/500') + s.get(httpbin('status/500')) if __name__ == '__main__': unittest.main() From bf2e73522f09dd131c5c8f78efd3ef6d6bc1d4d7 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Mon, 8 Dec 2014 13:04:27 -0500 Subject: [PATCH 0005/1803] Enable GitHub syntax highlighting on README --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 521ab6f209..e9f63ef83b 100644 --- a/README.rst +++ b/README.rst @@ -19,7 +19,7 @@ perform the simplest of tasks. Things shouldn't be this way. Not in Python. -.. code-block:: pycon +.. code-block:: python >>> r = requests.get('https://api.github.com', auth=('user', 'pass')) >>> r.status_code From 508f4b1ca514b2f18adaba807eaa81e0c298b8e1 Mon Sep 17 00:00:00 2001 From: rakesh Date: Tue, 9 Dec 2014 01:59:55 +0530 Subject: [PATCH 0006/1803] Updated the broken link to twitter streaming API documentation --- 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 6ec61eac16..ed990666dc 100644 --- a/docs/user/advanced.rst +++ b/docs/user/advanced.rst @@ -384,7 +384,7 @@ Streaming Requests With :class:`requests.Response.iter_lines()` you can easily iterate over streaming APIs such as the `Twitter Streaming -API `_. Simply +API `_. Simply set ``stream`` to ``True`` and iterate over the response with :class:`~requests.Response.iter_lines()`:: From d61540551943df57aa0dece5e44e130309dcafec Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Mon, 8 Dec 2014 20:48:40 -0600 Subject: [PATCH 0007/1803] Copy pip's import machinery wholesale --- requests/packages/__init__.py | 79 ++++++++++++++++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/requests/packages/__init__.py b/requests/packages/__init__.py index d62c4b7111..5c329d6f51 100644 --- a/requests/packages/__init__.py +++ b/requests/packages/__init__.py @@ -1,3 +1,80 @@ +""" +pip._vendor is for vendoring dependencies of pip to prevent needing pip to +depend on something external. + +Files inside of pip._vendor should be considered immutable and should only be +updated to versions from upstream. +""" from __future__ import absolute_import -from . import urllib3 +import sys + + +class VendorAlias(object): + + def __init__(self): + self._vendor_name = __name__ + self._vendor_pkg = self._vendor_name + "." + + def find_module(self, fullname, path=None): + if fullname.startswith(self._vendor_pkg): + return self + + def load_module(self, name): + # Ensure that this only works for the vendored name + if not name.startswith(self._vendor_pkg): + raise ImportError( + "Cannot import %s, must be a subpackage of '%s'." % ( + name, self._vendor_name, + ) + ) + + # Check to see if we already have this item in sys.modules, if we do + # then simply return that. + if name in sys.modules: + return sys.modules[name] + + # Check to see if we can import the vendor name + try: + # We do this dance here because we want to try and import this + # module without hitting a recursion error because of a bunch of + # VendorAlias instances on sys.meta_path + real_meta_path = sys.meta_path[:] + try: + sys.meta_path = [ + m for m in sys.meta_path + if not isinstance(m, VendorAlias) + ] + __import__(name) + module = sys.modules[name] + finally: + # Re-add any additions to sys.meta_path that were made while + # during the import we just did, otherwise things like + # pip._vendor.six.moves will fail. + for m in sys.meta_path: + if m not in real_meta_path: + real_meta_path.append(m) + + # Restore sys.meta_path with any new items. + sys.meta_path = real_meta_path + except ImportError: + # We can't import the vendor name, so we'll try to import the + # "real" name. + real_name = name[len(self._vendor_pkg):] + try: + __import__(real_name) + module = sys.modules[real_name] + except ImportError: + raise ImportError("No module named '%s'" % (name,)) + + # If we've gotten here we've found the module we're looking for, either + # as part of our vendored package, or as the real name, so we'll add + # it to sys.modules as the vendored name so that we don't have to do + # the lookup again. + sys.modules[name] = module + + # Finally, return the loaded module + return module + + +sys.meta_path.append(VendorAlias()) From e8d02ea0bbc05042e618a7ca115f4fca7b2deeb9 Mon Sep 17 00:00:00 2001 From: Arthur Darcet Date: Fri, 12 Dec 2014 16:11:32 +0100 Subject: [PATCH 0008/1803] utils.guess_filename fails if the given parameter looks like a file object but has a non-string name attribute e.g. a cherrypy uploaded file behave like a regular file, except that its name attribute is an int and passing it directly to requests fails because of that --- AUTHORS.rst | 1 + requests/utils.py | 2 +- test_requests.py | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/AUTHORS.rst b/AUTHORS.rst index 71171d0821..3f2a4d308a 100644 --- a/AUTHORS.rst +++ b/AUTHORS.rst @@ -158,3 +158,4 @@ Patches and Suggestions - Joe Alcorn (`@buttscicles `_) - Syed Suhail Ahmed (`@syedsuhail `_) - Scott Sadler (`@ssadler `_) +- Arthur Darcet (`@arthurdarcet `_) diff --git a/requests/utils.py b/requests/utils.py index aa5c140e58..7467941447 100644 --- a/requests/utils.py +++ b/requests/utils.py @@ -115,7 +115,7 @@ def get_netrc_auth(url): def guess_filename(obj): """Tries to guess the filename of the given object.""" name = getattr(obj, 'name', None) - if name and name[0] != '<' and name[-1] != '>': + if name and isinstance(name, builtin_str) and name[0] != '<' and name[-1] != '>': return os.path.basename(name) diff --git a/test_requests.py b/test_requests.py index 2d3ee628c5..68ee08c56f 100755 --- a/test_requests.py +++ b/test_requests.py @@ -928,6 +928,14 @@ def test_can_send_nonstring_objects_with_files(self): assert 'multipart/form-data' in p.headers['Content-Type'] + def test_can_send_file_object_with_non_string_filename(self): + f = io.BytesIO() + f.name = 2 + r = requests.Request('POST', httpbin('post'), files={'f': f}) + p = r.prepare() + + assert 'multipart/form-data' in p.headers['Content-Type'] + def test_autoset_header_values_are_native(self): data = 'this is a string' length = '16' From da82fb4b1d287caff3a2a99692be111eccbd1347 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Fri, 12 Dec 2014 21:12:31 -0600 Subject: [PATCH 0009/1803] Give proper attribution to pip --- requests/packages/__init__.py | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/requests/packages/__init__.py b/requests/packages/__init__.py index 5c329d6f51..ec6a9e0646 100644 --- a/requests/packages/__init__.py +++ b/requests/packages/__init__.py @@ -1,9 +1,24 @@ """ -pip._vendor is for vendoring dependencies of pip to prevent needing pip to -depend on something external. +Copyright (c) Donald Stufft, pip, and individual contributors -Files inside of pip._vendor should be considered immutable and should only be -updated to versions from upstream. +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from __future__ import absolute_import @@ -50,7 +65,7 @@ def load_module(self, name): finally: # Re-add any additions to sys.meta_path that were made while # during the import we just did, otherwise things like - # pip._vendor.six.moves will fail. + # requests.packages.urllib3.poolmanager will fail. for m in sys.meta_path: if m not in real_meta_path: real_meta_path.append(m) From 01b58ba04e694384119e33b05726b81757022b2e Mon Sep 17 00:00:00 2001 From: Ben Edelman Date: Mon, 15 Dec 2014 01:37:19 -0500 Subject: [PATCH 0010/1803] Fix a typo in a comment I just fixed a minor typo: "throws" is misspelled as "thows". --- requests/compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/compat.py b/requests/compat.py index be5a1ed6c1..c07726ee45 100644 --- a/requests/compat.py +++ b/requests/compat.py @@ -76,7 +76,7 @@ try: import simplejson as json except (ImportError, SyntaxError): - # simplejson does not support Python 3.2, it thows a SyntaxError + # simplejson does not support Python 3.2, it throws a SyntaxError # because of u'...' Unicode literals. import json From 4c61fef13f53db220f95032b72e6e374970bf272 Mon Sep 17 00:00:00 2001 From: ContinuousFunction Date: Mon, 15 Dec 2014 13:41:10 -0500 Subject: [PATCH 0011/1803] Uncommented test in test_requests.py Uncommented test_sesion_pickling in test_requests.py and ran the file to make sure the test passes. --- test_requests.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test_requests.py b/test_requests.py index 51d071e5d1..34348d3e47 100755 --- a/test_requests.py +++ b/test_requests.py @@ -825,15 +825,15 @@ def test_http_error(self): assert str(error) == 'message' assert error.response == response -## def test_session_pickling(self): -## r = requests.Request('GET', httpbin('get')) -## s = requests.Session() -## -## s = pickle.loads(pickle.dumps(s)) -## s.proxies = getproxies() -## -## r = s.send(r.prepare()) -## assert r.status_code == 200 + def test_session_pickling(self): + r = requests.Request('GET', httpbin('get')) + s = requests.Session() + + s = pickle.loads(pickle.dumps(s)) + s.proxies = getproxies() + + r = s.send(r.prepare()) + assert r.status_code == 200 def test_fixes_1329(self): """ From 925e975295e2fe772268e748c6c074af7b0ae47d Mon Sep 17 00:00:00 2001 From: daftshady Date: Tue, 16 Dec 2014 16:55:13 +0900 Subject: [PATCH 0012/1803] catch exception more specifically in Response.ok --- requests/models.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/requests/models.py b/requests/models.py index 2370b67f68..17ff466077 100644 --- a/requests/models.py +++ b/requests/models.py @@ -22,9 +22,8 @@ from .packages.urllib3.exceptions import ( DecodeError, ReadTimeoutError, ProtocolError) from .exceptions import ( - HTTPError, RequestException, MissingSchema, InvalidURL, - ChunkedEncodingError, ContentDecodingError, ConnectionError, - StreamConsumedError) + HTTPError, MissingSchema, InvalidURL, ChunkedEncodingError, + ContentDecodingError, ConnectionError, StreamConsumedError) from .utils import ( guess_filename, get_auth_from_url, requote_uri, stream_decode_response_unicode, to_key_val_list, parse_header_links, @@ -615,7 +614,7 @@ def __iter__(self): def ok(self): try: self.raise_for_status() - except RequestException: + except HTTPError: return False return True From bd3cf95e34aa49c8d764c899672048df107e0d70 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 16 Dec 2014 22:49:24 -0600 Subject: [PATCH 0013/1803] Fix error handling on Python 3 --- requests/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requests/models.py b/requests/models.py index 22b6d11046..b728c84e41 100644 --- a/requests/models.py +++ b/requests/models.py @@ -353,7 +353,7 @@ def prepare_url(self, url, params): try: scheme, auth, host, port, path, query, fragment = parse_url(url) except LocationParseError as e: - raise InvalidURL(e.message) + raise InvalidURL(*e.args) if not scheme: raise MissingSchema("Invalid URL {0!r}: No schema supplied. " From 86c3ecfd341e4e86977317f03ae344d363c63e3a Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 23 Dec 2014 10:40:31 -0600 Subject: [PATCH 0014/1803] Fix bug in renegotiating a nonce with the server If a session runs long enough (without constant activity) then the server can expire the nonce the session has negotiated. If that happens the session will get a new 401 response which we were immediately returning to the user. A user would then have to essentially reinitialize session.auth each time they get an unexpected 401. Also, there's no need for setattr calls when we can simply assign the attribute on the instance. --- requests/auth.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/requests/auth.py b/requests/auth.py index 618a902a29..b950181d9e 100644 --- a/requests/auth.py +++ b/requests/auth.py @@ -67,6 +67,7 @@ def __init__(self, username, password): self.nonce_count = 0 self.chal = {} self.pos = None + self.num_401_calls = 1 def build_digest_header(self, method, url): @@ -154,7 +155,7 @@ def sha_utf8(x): def handle_redirect(self, r, **kwargs): """Reset num_401_calls counter on redirects.""" if r.is_redirect: - setattr(self, 'num_401_calls', 1) + self.num_401_calls = 1 def handle_401(self, r, **kwargs): """Takes the given response and tries digest-auth, if needed.""" @@ -168,7 +169,7 @@ def handle_401(self, r, **kwargs): if 'digest' in s_auth.lower() and num_401_calls < 2: - setattr(self, 'num_401_calls', num_401_calls + 1) + self.num_401_calls += 1 pat = re.compile(r'digest ', flags=re.IGNORECASE) self.chal = parse_dict_header(pat.sub('', s_auth, count=1)) @@ -188,7 +189,7 @@ def handle_401(self, r, **kwargs): return _r - setattr(self, 'num_401_calls', num_401_calls + 1) + self.num_401_calls = 1 return r def __call__(self, r): From ce5b5fe227f939cd0195aa47822b8404357ced5d Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 23 Dec 2014 11:50:31 -0600 Subject: [PATCH 0015/1803] Add release notes for 2.5.1 --- HISTORY.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/HISTORY.rst b/HISTORY.rst index 5828c9d294..9f7161c622 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,19 @@ Release History --------------- +2.5.1 (2014-12-23) +++++++++++++++++++ + +**Behavioural Changes** + +- Only catch HTTPErrors in raise_for_status (#2382) + +**Bugfixes** + +- Handle LocationParseError from urllib3 (#2344) +- Handle file-like object filenames that are not strings (#2379) +- Unbreak HTTPDigestAuth handler. Allow new nonces to be negotiated (#2389) + 2.5.0 (2014-12-01) ++++++++++++++++++ From b83131779c701720a9ae9efae78996277d416269 Mon Sep 17 00:00:00 2001 From: Ian Cordasco Date: Tue, 23 Dec 2014 11:54:01 -0600 Subject: [PATCH 0016/1803] Bump version to 2.5.1 --- requests/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requests/__init__.py b/requests/__init__.py index 22cd57d1f6..ac2b06c86c 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -42,8 +42,8 @@ """ __title__ = 'requests' -__version__ = '2.5.0' -__build__ = 0x020500 +__version__ = '2.5.1' +__build__ = 0x020501 __author__ = 'Kenneth Reitz' __license__ = 'Apache 2.0' __copyright__ = 'Copyright 2014 Kenneth Reitz' From f4ec3d2309c681777211c5aba8c9fe3144f21ae2 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Wed, 24 Dec 2014 14:33:14 +0000 Subject: [PATCH 0017/1803] Clean up cookie docs and display them. --- docs/api.rst | 6 ++++ requests/cookies.py | 88 ++++++++++++++++++++++++--------------------- 2 files changed, 54 insertions(+), 40 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 7c5dae2e54..7225a83799 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -88,6 +88,12 @@ Cookies .. autofunction:: requests.utils.cookiejar_from_dict .. autofunction:: requests.utils.add_dict_to_cookiejar +.. autoclass:: requests.cookies.RequestsCookieJar + :inherited-members: + +.. autoclass:: requests.cookies.CookieConflictError + :inherited-members: + Encodings ~~~~~~~~~ diff --git a/requests/cookies.py b/requests/cookies.py index 831c49c6d2..a972c6430a 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -157,26 +157,28 @@ class CookieConflictError(RuntimeError): class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): - """Compatibility class; is a cookielib.CookieJar, but exposes a dict interface. + """Compatibility class; is a cookielib.CookieJar, but exposes a dict + interface. This is the CookieJar we create by default for requests and sessions that don't specify one, since some clients may expect response.cookies and session.cookies to support dict operations. - Don't use the dict interface internally; it's just for compatibility with - with external client code. All `requests` code should work out of the box - with externally provided instances of CookieJar, e.g., LWPCookieJar and - FileCookieJar. - - Caution: dictionary operations that are normally O(1) may be O(n). + Requests does not use the dict interface internally; it's just for + compatibility with external client code. All `requests` code should work + out of the box with externally provided instances of ``CookieJar``, e.g. + ``LWPCookieJar`` and ``FileCookieJar``. Unlike a regular CookieJar, this class is pickleable. - """ + .. warning:: dictionary operations that are normally O(1) may be O(n). + """ def get(self, name, default=None, domain=None, path=None): """Dict-like get() that also supports optional domain and path args in order to resolve naming collisions from using one cookie jar over - multiple domains. Caution: operation is O(n), not O(1).""" + multiple domains. + + .. warning:: operation is O(n), not O(1).""" try: return self._find_no_duplicates(name, domain, path) except KeyError: @@ -199,37 +201,38 @@ def set(self, name, value, **kwargs): return c def iterkeys(self): - """Dict-like iterkeys() that returns an iterator of names of cookies from the jar. - See itervalues() and iteritems().""" + """Dict-like iterkeys() that returns an iterator of names of cookies + from the jar. See itervalues() and iteritems().""" for cookie in iter(self): yield cookie.name def keys(self): - """Dict-like keys() that returns a list of names of cookies from the jar. - See values() and items().""" + """Dict-like keys() that returns a list of names of cookies from the + jar. See values() and items().""" return list(self.iterkeys()) def itervalues(self): - """Dict-like itervalues() that returns an iterator of values of cookies from the jar. - See iterkeys() and iteritems().""" + """Dict-like itervalues() that returns an iterator of values of cookies + from the jar. See iterkeys() and iteritems().""" for cookie in iter(self): yield cookie.value def values(self): - """Dict-like values() that returns a list of values of cookies from the jar. - See keys() and items().""" + """Dict-like values() that returns a list of values of cookies from the + jar. See keys() and items().""" return list(self.itervalues()) def iteritems(self): - """Dict-like iteritems() that returns an iterator of name-value tuples from the jar. - See iterkeys() and itervalues().""" + """Dict-like iteritems() that returns an iterator of name-value tuples + from the jar. See iterkeys() and itervalues().""" for cookie in iter(self): yield cookie.name, cookie.value def items(self): - """Dict-like items() that returns a list of name-value tuples from the jar. - See keys() and values(). Allows client-code to call "dict(RequestsCookieJar) - and get a vanilla python dict of key value pairs.""" + """Dict-like items() that returns a list of name-value tuples from the + jar. See keys() and values(). Allows client-code to call + ``dict(RequestsCookieJar)`` and get a vanilla python dict of key value + pairs.""" return list(self.iteritems()) def list_domains(self): @@ -259,8 +262,9 @@ def multiple_domains(self): return False # there is only one domain in jar def get_dict(self, domain=None, path=None): - """Takes as an argument an optional domain and path and returns a plain old - Python dict of name-value pairs of cookies that meet the requirements.""" + """Takes as an argument an optional domain and path and returns a plain + old Python dict of name-value pairs of cookies that meet the + requirements.""" dictionary = {} for cookie in iter(self): if (domain is None or cookie.domain == domain) and (path is None @@ -269,21 +273,23 @@ def get_dict(self, domain=None, path=None): return dictionary def __getitem__(self, name): - """Dict-like __getitem__() for compatibility with client code. Throws exception - if there are more than one cookie with name. In that case, use the more - explicit get() method instead. Caution: operation is O(n), not O(1).""" + """Dict-like __getitem__() for compatibility with client code. Throws + exception if there are more than one cookie with name. In that case, + use the more explicit get() method instead. Caution: operation is O(n), + not O(1).""" return self._find_no_duplicates(name) def __setitem__(self, name, value): - """Dict-like __setitem__ for compatibility with client code. Throws exception - if there is already a cookie of that name in the jar. In that case, use the more - explicit set() method instead.""" + """Dict-like __setitem__ for compatibility with client code. Throws + exception if there is already a cookie of that name in the jar. In that + case, use the more explicit set() method instead.""" self.set(name, value) def __delitem__(self, name): - """Deletes a cookie given a name. Wraps cookielib.CookieJar's remove_cookie_by_name().""" + """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): @@ -300,10 +306,11 @@ def update(self, other): super(RequestsCookieJar, self).update(other) def _find(self, name, domain=None, path=None): - """Requests uses this method internally to get cookie values. Takes as args name - and optional domain and path. Returns a cookie.value. If there are conflicting cookies, - _find arbitrarily chooses one. See _find_no_duplicates if you want an exception thrown - if there are conflicting cookies.""" + """Requests uses this method internally to get cookie values. Takes as + args name and optional domain and path. Returns a cookie.value. If + there are conflicting cookies, _find arbitrarily chooses one. See + _find_no_duplicates if you want an exception thrown if there are + conflicting cookies.""" for cookie in iter(self): if cookie.name == name: if domain is None or cookie.domain == domain: @@ -313,10 +320,11 @@ def _find(self, name, domain=None, path=None): raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) def _find_no_duplicates(self, name, domain=None, path=None): - """__get_item__ and get call _find_no_duplicates -- never used in Requests internally. - Takes as args name and optional domain and path. Returns a cookie.value. - Throws KeyError if cookie is not found and CookieConflictError if there are - multiple cookies that match name and optionally domain and path.""" + """__get_item__ and get call _find_no_duplicates -- never used in + Requests internally. Takes as args name and optional domain and path. + Returns a cookie.value. Throws KeyError if cookie is not found and + CookieConflictError if there are multiple cookies that match name and + optionally domain and path.""" toReturn = None for cookie in iter(self): if cookie.name == name: @@ -440,7 +448,7 @@ def merge_cookies(cookiejar, cookies): """ if not isinstance(cookiejar, cookielib.CookieJar): raise ValueError('You can only merge into CookieJar') - + if isinstance(cookies, dict): cookiejar = cookiejar_from_dict( cookies, cookiejar=cookiejar, overwrite=False) From 101425ebfc747d4c0173fc42e64654c6bcb45fe1 Mon Sep 17 00:00:00 2001 From: Cory Benfield Date: Wed, 24 Dec 2014 14:54:49 +0000 Subject: [PATCH 0018/1803] Enhance documentation for clarity. --- requests/cookies.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/requests/cookies.py b/requests/cookies.py index a972c6430a..6969fe5cc4 100644 --- a/requests/cookies.py +++ b/requests/cookies.py @@ -165,7 +165,7 @@ class RequestsCookieJar(cookielib.CookieJar, collections.MutableMapping): session.cookies to support dict operations. Requests does not use the dict interface internally; it's just for - compatibility with external client code. All `requests` code should work + compatibility with external client code. All requests code should work out of the box with externally provided instances of ``CookieJar``, e.g. ``LWPCookieJar`` and ``FileCookieJar``. @@ -275,8 +275,9 @@ def get_dict(self, domain=None, path=None): def __getitem__(self, name): """Dict-like __getitem__() for compatibility with client code. Throws exception if there are more than one cookie with name. In that case, - use the more explicit get() method instead. Caution: operation is O(n), - not O(1).""" + use the more explicit get() method instead. + + .. warning:: operation is O(n), not O(1).""" return self._find_no_duplicates(name) @@ -320,11 +321,11 @@ def _find(self, name, domain=None, path=None): raise KeyError('name=%r, domain=%r, path=%r' % (name, domain, path)) def _find_no_duplicates(self, name, domain=None, path=None): - """__get_item__ and get call _find_no_duplicates -- never used in - Requests internally. Takes as args name and optional domain and path. - Returns a cookie.value. Throws KeyError if cookie is not found and - CookieConflictError if there are multiple cookies that match name and - optionally domain and path.""" + """Both ``__get_item__`` and ``get`` call this function: it's never + used elsewhere in Requests. Takes as args name and optional domain and + path. Returns a cookie.value. Throws KeyError if cookie is not found + and CookieConflictError if there are multiple cookies that match name + and optionally domain and path.""" toReturn = None for cookie in iter(self): if cookie.name == name: From 892f2560c1fc99260a8cb073478c0c6e7245bd93 Mon Sep 17 00:00:00 2001 From: Shrayas Date: Thu, 1 Jan 2015 09:04:50 +0530 Subject: [PATCH 0019/1803] Changing year in all copyright information - Happy new year! Thanks for this awesome library :) --- LICENSE | 2 +- docs/conf.py | 2 +- requests/__init__.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/LICENSE b/LICENSE index 8c5e758401..a103fc915e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2014 Kenneth Reitz +Copyright 2015 Kenneth Reitz Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/docs/conf.py b/docs/conf.py index 4521eed4c4..4969857b34 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -46,7 +46,7 @@ # General information about the project. project = u'Requests' -copyright = u'2014. A Kenneth Reitz Project' +copyright = u'2015. A Kenneth Reitz Project' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/requests/__init__.py b/requests/__init__.py index ac2b06c86c..0ec356603c 100644 --- a/requests/__init__.py +++ b/requests/__init__.py @@ -36,7 +36,7 @@ The other HTTP methods are supported - see `requests.api`. Full documentation is at . -:copyright: (c) 2014 by Kenneth Reitz. +:copyright: (c) 2015 by Kenneth Reitz. :license: Apache 2.0, see LICENSE for more details. """ @@ -46,7 +46,7 @@ __build__ = 0x020501 __author__ = 'Kenneth Reitz' __license__ = 'Apache 2.0' -__copyright__ = 'Copyright 2014 Kenneth Reitz' +__copyright__ = 'Copyright 2015 Kenneth Reitz' # Attempt to enable urllib3's SNI support, if possible try: From 673bd6afce7ca407c1863be5f6049edd4f5d43b0 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Tue, 6 Jan 2015 00:56:47 -0500 Subject: [PATCH 0020/1803] javascripts --- docs/_themes/kr/layout.html | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/docs/_themes/kr/layout.html b/docs/_themes/kr/layout.html index cf977d8fea..1b7a67c2f1 100644 --- a/docs/_themes/kr/layout.html +++ b/docs/_themes/kr/layout.html @@ -68,5 +68,15 @@ })(); + + {%- endblock %} From 15597c27cd62c56892963545c526be7dcdd7854d Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 8 Jan 2015 09:52:50 -0500 Subject: [PATCH 0021/1803] Requests Pro --- docs/_templates/sidebarintro.html | 8 ++------ docs/_templates/sidebarlogo.html | 3 ++- docs/_themes/kr/layout.html | 3 +++ 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index e60cf4518e..d12a44071f 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -17,12 +17,8 @@

Donate

- If you love Requests, consider supporting the author on Gratipay: -

-

- + If you love Requests, consider supporting the author: + Buy Requests Pro

diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html index 930fdb3d41..181d70f396 100644 --- a/docs/_templates/sidebarlogo.html +++ b/docs/_templates/sidebarlogo.html @@ -17,7 +17,8 @@

Donate

- If you love Requests, consider supporting the author on Gittip: + If you love Requests, consider supporting the author: + Buy Requests Pro

-

Get Updates

Receive updates on new releases and upcoming projects.

From ac34e3b346aa22d16a34bf869be1343d0d4236f1 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 8 Jan 2015 09:55:40 -0500 Subject: [PATCH 0023/1803] simpler button --- docs/_templates/sidebarintro.html | 8 +++----- docs/_templates/sidebarlogo.html | 9 +++------ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index d12a44071f..8e46fbc9cb 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -15,11 +15,9 @@

-

Donate

-

- If you love Requests, consider supporting the author: - Buy Requests Pro -

+

+ Buy Requests Pro +

Get Updates

diff --git a/docs/_templates/sidebarlogo.html b/docs/_templates/sidebarlogo.html index 3ac39151e0..928cd2fdb8 100644 --- a/docs/_templates/sidebarlogo.html +++ b/docs/_templates/sidebarlogo.html @@ -14,12 +14,9 @@ development release.

- -

Donate

-

- If you love Requests, consider supporting the author: - Buy Requests Pro -

+

+ Buy Requests Pro +

Get Updates

Receive updates on new releases and upcoming projects.

From 09658b80c0a090f54e8a30a7531deb96ff8da11e Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Thu, 8 Jan 2015 09:58:31 -0500 Subject: [PATCH 0024/1803] certifi --- docs/_templates/sidebarintro.html | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index 8e46fbc9cb..9ed32a0cf3 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -40,6 +40,7 @@

Translations

Useful Links