Update requests lib
This commit is contained in:
+104
-53
@@ -12,8 +12,9 @@ import os
|
||||
from collections import Mapping
|
||||
from datetime import datetime
|
||||
|
||||
from .compat import cookielib, OrderedDict, urljoin, urlparse
|
||||
from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar
|
||||
from .compat import cookielib, OrderedDict, urljoin, urlparse, builtin_str
|
||||
from .cookies import (
|
||||
cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies)
|
||||
from .models import Request, PreparedRequest
|
||||
from .hooks import default_hooks, dispatch_hook
|
||||
from .utils import to_key_val_list, default_headers
|
||||
@@ -65,21 +66,32 @@ def merge_setting(request_setting, session_setting, dict_class=OrderedDict):
|
||||
return merged_setting
|
||||
|
||||
|
||||
def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict):
|
||||
"""
|
||||
Properly merges both requests and session hooks.
|
||||
|
||||
This is necessary because when request_hooks == {'response': []}, the
|
||||
merge breaks Session hooks entirely.
|
||||
"""
|
||||
if session_hooks is None or session_hooks.get('response') == []:
|
||||
return request_hooks
|
||||
|
||||
if request_hooks is None or request_hooks.get('response') == []:
|
||||
return session_hooks
|
||||
|
||||
return merge_setting(request_hooks, session_hooks, dict_class)
|
||||
|
||||
|
||||
class SessionRedirectMixin(object):
|
||||
def resolve_redirects(self, resp, req, stream=False, timeout=None,
|
||||
verify=True, cert=None, proxies=None):
|
||||
"""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)):
|
||||
while ('location' in resp.headers and resp.status_code in REDIRECT_STATI):
|
||||
prepared_request = req.copy()
|
||||
|
||||
resp.content # Consume socket so it can be released
|
||||
|
||||
@@ -90,13 +102,17 @@ 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('//'):
|
||||
parsed_rurl = urlparse(resp.url)
|
||||
url = '%s:%s' % (parsed_rurl.scheme, url)
|
||||
|
||||
# The scheme should be lower case...
|
||||
parsed = urlparse(url)
|
||||
url = parsed.geturl()
|
||||
|
||||
# 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.
|
||||
@@ -109,12 +125,17 @@ 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')):
|
||||
# First, turn 302s into GETs.
|
||||
if resp.status_code == codes.found and method != 'HEAD':
|
||||
method = 'GET'
|
||||
|
||||
# Second, if a POST is responded to with a 301, turn it into a GET.
|
||||
# This bizarre behaviour is explained in Issue 1704.
|
||||
if resp.status_code == codes.moved and method == 'POST':
|
||||
method = 'GET'
|
||||
|
||||
prepared_request.method = method
|
||||
@@ -132,7 +153,9 @@ class SessionRedirectMixin(object):
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
prepared_request.prepare_cookies(self.cookies)
|
||||
extract_cookies_to_jar(prepared_request._cookies,
|
||||
prepared_request, resp.raw)
|
||||
prepared_request.prepare_cookies(prepared_request._cookies)
|
||||
|
||||
resp = self.send(
|
||||
prepared_request,
|
||||
@@ -153,7 +176,7 @@ class SessionRedirectMixin(object):
|
||||
class Session(SessionRedirectMixin):
|
||||
"""A Requests session.
|
||||
|
||||
Provides cookie persistience, connection-pooling, and configuration.
|
||||
Provides cookie persistence, connection-pooling, and configuration.
|
||||
|
||||
Basic Usage::
|
||||
|
||||
@@ -208,7 +231,10 @@ class Session(SessionRedirectMixin):
|
||||
#: Should we trust the environment?
|
||||
self.trust_env = True
|
||||
|
||||
# Set up a CookieJar to be used by default
|
||||
#: A CookieJar containing all currently outstanding cookies set on this
|
||||
#: session. By default it is a
|
||||
#: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
|
||||
#: may be any other ``cookielib.CookieJar`` compatible object.
|
||||
self.cookies = cookiejar_from_dict({})
|
||||
|
||||
# Default connection adapters.
|
||||
@@ -222,6 +248,45 @@ class Session(SessionRedirectMixin):
|
||||
def __exit__(self, *args):
|
||||
self.close()
|
||||
|
||||
def prepare_request(self, request):
|
||||
"""Constructs a :class:`PreparedRequest <PreparedRequest>` for
|
||||
transmission and returns it. The :class:`PreparedRequest` has settings
|
||||
merged from the :class:`Request <Request>` instance and those of the
|
||||
:class:`Session`.
|
||||
|
||||
:param request: :class:`Request` instance to prepare with this
|
||||
session's settings.
|
||||
"""
|
||||
cookies = request.cookies or {}
|
||||
|
||||
# Bootstrap CookieJar.
|
||||
if not isinstance(cookies, cookielib.CookieJar):
|
||||
cookies = cookiejar_from_dict(cookies)
|
||||
|
||||
# Merge with session cookies
|
||||
merged_cookies = merge_cookies(
|
||||
merge_cookies(RequestsCookieJar(), self.cookies), cookies)
|
||||
|
||||
|
||||
# Set environment's basic authentication if not explicitly set.
|
||||
auth = request.auth
|
||||
if self.trust_env and not auth and not self.auth:
|
||||
auth = get_netrc_auth(request.url)
|
||||
|
||||
p = PreparedRequest()
|
||||
p.prepare(
|
||||
method=request.method.upper(),
|
||||
url=request.url,
|
||||
files=request.files,
|
||||
data=request.data,
|
||||
headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict),
|
||||
params=merge_setting(request.params, self.params),
|
||||
auth=merge_setting(auth, self.auth),
|
||||
cookies=merged_cookies,
|
||||
hooks=merge_hooks(request.hooks, self.hooks),
|
||||
)
|
||||
return p
|
||||
|
||||
def request(self, method, url,
|
||||
params=None,
|
||||
data=None,
|
||||
@@ -266,19 +331,24 @@ class Session(SessionRedirectMixin):
|
||||
If Tuple, ('cert', 'key') pair.
|
||||
"""
|
||||
|
||||
cookies = cookies or {}
|
||||
method = builtin_str(method)
|
||||
|
||||
# Create the Request.
|
||||
req = Request(
|
||||
method = method.upper(),
|
||||
url = url,
|
||||
headers = headers,
|
||||
files = files,
|
||||
data = data or {},
|
||||
params = params or {},
|
||||
auth = auth,
|
||||
cookies = cookies,
|
||||
hooks = hooks,
|
||||
)
|
||||
prep = self.prepare_request(req)
|
||||
|
||||
proxies = proxies or {}
|
||||
|
||||
# Bootstrap CookieJar.
|
||||
if not isinstance(cookies, cookielib.CookieJar):
|
||||
cookies = cookiejar_from_dict(cookies)
|
||||
|
||||
# Merge with session cookies
|
||||
merged_cookies = RequestsCookieJar()
|
||||
merged_cookies.update(self.cookies)
|
||||
merged_cookies.update(cookies)
|
||||
cookies = merged_cookies
|
||||
|
||||
# Gather clues from the surrounding environment.
|
||||
if self.trust_env:
|
||||
# Set environment's proxies.
|
||||
@@ -286,10 +356,6 @@ class Session(SessionRedirectMixin):
|
||||
for (k, v) in env_proxies.items():
|
||||
proxies.setdefault(k, v)
|
||||
|
||||
# Set environment's basic authentication.
|
||||
if not auth:
|
||||
auth = get_netrc_auth(url)
|
||||
|
||||
# Look for configuration.
|
||||
if not verify and verify is not False:
|
||||
verify = os.environ.get('REQUESTS_CA_BUNDLE')
|
||||
@@ -299,30 +365,11 @@ class Session(SessionRedirectMixin):
|
||||
verify = os.environ.get('CURL_CA_BUNDLE')
|
||||
|
||||
# Merge all the kwargs.
|
||||
params = merge_setting(params, self.params)
|
||||
headers = merge_setting(headers, self.headers, dict_class=CaseInsensitiveDict)
|
||||
auth = merge_setting(auth, self.auth)
|
||||
proxies = merge_setting(proxies, self.proxies)
|
||||
hooks = merge_setting(hooks, self.hooks)
|
||||
stream = merge_setting(stream, self.stream)
|
||||
verify = merge_setting(verify, self.verify)
|
||||
cert = merge_setting(cert, self.cert)
|
||||
|
||||
# Create the Request.
|
||||
req = Request()
|
||||
req.method = method.upper()
|
||||
req.url = url
|
||||
req.headers = headers
|
||||
req.files = files
|
||||
req.data = data
|
||||
req.params = params
|
||||
req.auth = auth
|
||||
req.cookies = cookies
|
||||
req.hooks = hooks
|
||||
|
||||
# Prepare the Request.
|
||||
prep = req.prepare()
|
||||
|
||||
# Send the request.
|
||||
send_kwargs = {
|
||||
'stream': stream,
|
||||
@@ -416,7 +463,7 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
# It's possible that users might accidentally send a Request object.
|
||||
# Guard against that specific failure case.
|
||||
if getattr(request, 'prepare', None):
|
||||
if not isinstance(request, PreparedRequest):
|
||||
raise ValueError('You can only send PreparedRequests.')
|
||||
|
||||
# Set up variables needed for resolve_redirects and dispatching of
|
||||
@@ -443,6 +490,10 @@ class Session(SessionRedirectMixin):
|
||||
r = dispatch_hook('response', hooks, r, **kwargs)
|
||||
|
||||
# Persist cookies
|
||||
if r.history:
|
||||
# If the hooks create history then we want those cookies too
|
||||
for resp in r.history:
|
||||
extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
|
||||
extract_cookies_to_jar(self.cookies, request, r.raw)
|
||||
|
||||
# Redirect resolving generator.
|
||||
@@ -467,7 +518,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 :-/
|
||||
@@ -475,7 +526,7 @@ class Session(SessionRedirectMixin):
|
||||
|
||||
def close(self):
|
||||
"""Closes all adapters and as such the session"""
|
||||
for _, v in self.adapters.items():
|
||||
for v in self.adapters.values():
|
||||
v.close()
|
||||
|
||||
def mount(self, prefix, adapter):
|
||||
|
||||
Reference in New Issue
Block a user