From 653256249d44c67a0852d57a166948a9dc712ef4 Mon Sep 17 00:00:00 2001 From: SVN-Git Migration Date: Thu, 8 Oct 2015 13:41:28 -0700 Subject: Imported Upstream version 1.2.3 --- requests/sessions.py | 126 +++++++++++++++++++++++---------------------------- 1 file changed, 56 insertions(+), 70 deletions(-) (limited to 'requests/sessions.py') diff --git a/requests/sessions.py b/requests/sessions.py index de0d9d6..f4aeeee 100644 --- a/requests/sessions.py +++ b/requests/sessions.py @@ -9,16 +9,17 @@ requests (cookies, auth, proxies). """ import os +from collections import Mapping from datetime import datetime -from .compat import cookielib -from .cookies import cookiejar_from_dict +from .compat import cookielib, OrderedDict, urljoin, urlparse +from .cookies import cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar from .models import Request, PreparedRequest from .hooks import default_hooks, dispatch_hook -from .utils import from_key_val_list, default_headers +from .utils import to_key_val_list, default_headers from .exceptions import TooManyRedirects, InvalidSchema +from .structures import CaseInsensitiveDict -from .compat import urlparse, urljoin from .adapters import HTTPAdapter from .utils import requote_uri, get_environ_proxies, get_netrc_auth @@ -33,49 +34,35 @@ REDIRECT_STATI = ( DEFAULT_REDIRECT_LIMIT = 30 -def merge_kwargs(local_kwarg, default_kwarg): - """Merges kwarg dictionaries. - - If a local key in the dictionary is set to None, it will be removed. +def merge_setting(request_setting, session_setting, dict_class=OrderedDict): + """ + Determines appropriate setting for a given request, taking into account the + explicit setting on that request, and the setting in the session. If a + setting is a dictionary, they will be merged together using `dict_class` """ - if default_kwarg is None: - return local_kwarg - - if isinstance(local_kwarg, str): - return local_kwarg - - if local_kwarg is None: - return default_kwarg - - # Bypass if not a dictionary (e.g. timeout) - if not hasattr(default_kwarg, 'items'): - return local_kwarg + if session_setting is None: + return request_setting - default_kwarg = from_key_val_list(default_kwarg) - local_kwarg = from_key_val_list(local_kwarg) + if request_setting is None: + return session_setting - # Update new values in a case-insensitive way - def get_original_key(original_keys, new_key): - """ - Finds the key from original_keys that case-insensitive matches new_key. - """ - for original_key in original_keys: - if key.lower() == original_key.lower(): - return original_key - return new_key + # Bypass if not a dictionary (e.g. verify) + if not ( + isinstance(session_setting, Mapping) and + isinstance(request_setting, Mapping) + ): + return request_setting - kwargs = default_kwarg.copy() - original_keys = kwargs.keys() - for key, value in local_kwarg.items(): - kwargs[get_original_key(original_keys, key)] = value + merged_setting = dict_class(to_key_val_list(session_setting)) + merged_setting.update(to_key_val_list(request_setting)) # Remove keys that are set to None. - for (k, v) in local_kwarg.items(): + for (k, v) in request_setting.items(): if v is None: - del kwargs[k] + del merged_setting[k] - return kwargs + return merged_setting class SessionRedirectMixin(object): @@ -91,10 +78,6 @@ class SessionRedirectMixin(object): prepared_request.method = req.method prepared_request.url = req.url - cookiejar = cookiejar_from_dict({}) - cookiejar.update(self.cookies) - cookiejar.update(resp.cookies) - # ((resp.status_code is codes.see_other)) while (('location' in resp.headers and resp.status_code in REDIRECT_STATI)): @@ -116,9 +99,11 @@ class SessionRedirectMixin(object): # 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. if not urlparse(url).netloc: - # Compliant with RFC3986, we percent encode the url. url = urljoin(resp.url, requote_uri(url)) + else: + url = requote_uri(url) prepared_request.url = url @@ -129,7 +114,7 @@ class SessionRedirectMixin(object): # Do what the browsers do, despite standards... if (resp.status_code in (codes.moved, codes.found) and - prepared_request.method == 'POST'): + prepared_request.method not in ('GET', 'HEAD')): method = 'GET' prepared_request.method = method @@ -147,7 +132,7 @@ class SessionRedirectMixin(object): except KeyError: pass - prepared_request.prepare_cookies(cookiejar) + prepared_request.prepare_cookies(self.cookies) resp = self.send( prepared_request, @@ -159,13 +144,11 @@ class SessionRedirectMixin(object): allow_redirects=False, ) - cookiejar.update(resp.cookies) + extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) i += 1 yield resp - resp.cookies.update(cookiejar) - class Session(SessionRedirectMixin): """A Requests session. @@ -218,7 +201,8 @@ class Session(SessionRedirectMixin): #: SSL certificate default. self.cert = None - #: Maximum number of redirects to follow. + #: Maximum number of redirects allowed. If the request exceeds this + #: limit, a :class:`TooManyRedirects` exception is raised. self.max_redirects = DEFAULT_REDIRECT_LIMIT #: Should we trust the environment? @@ -228,9 +212,9 @@ class Session(SessionRedirectMixin): self.cookies = cookiejar_from_dict({}) # Default connection adapters. - self.adapters = {} - self.mount('http://', HTTPAdapter()) + self.adapters = OrderedDict() self.mount('https://', HTTPAdapter()) + self.mount('http://', HTTPAdapter()) def __enter__(self): return self @@ -274,12 +258,8 @@ class Session(SessionRedirectMixin): :param allow_redirects: (optional) Boolean. Set to True by default. :param proxies: (optional) Dictionary mapping protocol to the URL of the proxy. - :param return_response: (optional) If False, an un-sent Request object - will returned. - :param config: (optional) A configuration dictionary. See - ``request.defaults`` for allowed keys and their default values. - :param prefetch: (optional) whether to immediately download the response - content. Defaults to ``True``. + :param stream: (optional) whether to immediately download the response + content. Defaults to ``False``. :param verify: (optional) if ``True``, the SSL cert will be verified. A CA_BUNDLE path can also be provided. :param cert: (optional) if String, path to ssl client cert file (.pem). @@ -294,7 +274,8 @@ class Session(SessionRedirectMixin): cookies = cookiejar_from_dict(cookies) # Merge with session cookies - merged_cookies = self.cookies.copy() + merged_cookies = RequestsCookieJar() + merged_cookies.update(self.cookies) merged_cookies.update(cookies) cookies = merged_cookies @@ -318,14 +299,14 @@ class Session(SessionRedirectMixin): verify = os.environ.get('CURL_CA_BUNDLE') # Merge all the kwargs. - params = merge_kwargs(params, self.params) - headers = merge_kwargs(headers, self.headers) - auth = merge_kwargs(auth, self.auth) - proxies = merge_kwargs(proxies, self.proxies) - hooks = merge_kwargs(hooks, self.hooks) - stream = merge_kwargs(stream, self.stream) - verify = merge_kwargs(verify, self.verify) - cert = merge_kwargs(cert, self.cert) + 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() @@ -353,9 +334,6 @@ class Session(SessionRedirectMixin): } resp = self.send(prep, **send_kwargs) - # Persist cookies. - self.cookies.update(resp.cookies) - return resp def get(self, url, **kwargs): @@ -464,6 +442,9 @@ class Session(SessionRedirectMixin): # Response manipulation hooks r = dispatch_hook('response', hooks, r, **kwargs) + # Persist cookies + extract_cookies_to_jar(self.cookies, request, r.raw) + # Redirect resolving generator. gen = self.resolve_redirects(r, request, stream=stream, timeout=timeout, verify=verify, cert=cert, @@ -498,8 +479,13 @@ class Session(SessionRedirectMixin): v.close() def mount(self, prefix, adapter): - """Registers a connection adapter to a prefix.""" + """Registers a connection adapter to a prefix. + + Adapters are sorted in descending order by key length.""" self.adapters[prefix] = adapter + keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] + for key in keys_to_move: + self.adapters[key] = self.adapters.pop(key) def __getstate__(self): return dict((attr, getattr(self, attr, None)) for attr in self.__attrs__) -- cgit v1.2.3