1# -*- coding: utf-8 -*- 2 3""" 4requests.sessions 5~~~~~~~~~~~~~~~~~ 6 7This module provides a Session object to manage and persist settings across 8requests (cookies, auth, proxies). 9""" 10import os 11import sys 12import time 13from datetime import timedelta 14from collections import OrderedDict 15 16from .auth import _basic_auth_str 17from .compat import cookielib, is_py3, urljoin, urlparse, Mapping 18from .cookies import ( 19 cookiejar_from_dict, extract_cookies_to_jar, RequestsCookieJar, merge_cookies) 20from .models import Request, PreparedRequest, DEFAULT_REDIRECT_LIMIT 21from .hooks import default_hooks, dispatch_hook 22from ._internal_utils import to_native_string 23from .utils import to_key_val_list, default_headers, DEFAULT_PORTS 24from .exceptions import ( 25 TooManyRedirects, InvalidSchema, ChunkedEncodingError, ContentDecodingError) 26 27from .structures import CaseInsensitiveDict 28from .adapters import HTTPAdapter 29 30from .utils import ( 31 requote_uri, get_environ_proxies, get_netrc_auth, should_bypass_proxies, 32 get_auth_from_url, rewind_body 33) 34 35from .status_codes import codes 36 37# formerly defined here, reexposed here for backward compatibility 38from .models import REDIRECT_STATI 39 40# Preferred clock, based on which one is more accurate on a given system. 41if sys.platform == 'win32': 42 try: # Python 3.4+ 43 preferred_clock = time.perf_counter 44 except AttributeError: # Earlier than Python 3. 45 preferred_clock = time.clock 46else: 47 preferred_clock = time.time 48 49 50def merge_setting(request_setting, session_setting, dict_class=OrderedDict): 51 """Determines appropriate setting for a given request, taking into account 52 the explicit setting on that request, and the setting in the session. If a 53 setting is a dictionary, they will be merged together using `dict_class` 54 """ 55 56 if session_setting is None: 57 return request_setting 58 59 if request_setting is None: 60 return session_setting 61 62 # Bypass if not a dictionary (e.g. verify) 63 if not ( 64 isinstance(session_setting, Mapping) and 65 isinstance(request_setting, Mapping) 66 ): 67 return request_setting 68 69 merged_setting = dict_class(to_key_val_list(session_setting)) 70 merged_setting.update(to_key_val_list(request_setting)) 71 72 # Remove keys that are set to None. Extract keys first to avoid altering 73 # the dictionary during iteration. 74 none_keys = [k for (k, v) in merged_setting.items() if v is None] 75 for key in none_keys: 76 del merged_setting[key] 77 78 return merged_setting 79 80 81def merge_hooks(request_hooks, session_hooks, dict_class=OrderedDict): 82 """Properly merges both requests and session hooks. 83 84 This is necessary because when request_hooks == {'response': []}, the 85 merge breaks Session hooks entirely. 86 """ 87 if session_hooks is None or session_hooks.get('response') == []: 88 return request_hooks 89 90 if request_hooks is None or request_hooks.get('response') == []: 91 return session_hooks 92 93 return merge_setting(request_hooks, session_hooks, dict_class) 94 95 96class SessionRedirectMixin(object): 97 98 def get_redirect_target(self, resp): 99 """Receives a Response. Returns a redirect URI or ``None``""" 100 # Due to the nature of how requests processes redirects this method will 101 # be called at least once upon the original response and at least twice 102 # on each subsequent redirect response (if any). 103 # If a custom mixin is used to handle this logic, it may be advantageous 104 # to cache the redirect location onto the response object as a private 105 # attribute. 106 if resp.is_redirect: 107 location = resp.headers['location'] 108 # Currently the underlying http module on py3 decode headers 109 # in latin1, but empirical evidence suggests that latin1 is very 110 # rarely used with non-ASCII characters in HTTP headers. 111 # It is more likely to get UTF8 header rather than latin1. 112 # This causes incorrect handling of UTF8 encoded location headers. 113 # To solve this, we re-encode the location in latin1. 114 if is_py3: 115 location = location.encode('latin1') 116 return to_native_string(location, 'utf8') 117 return None 118 119 def should_strip_auth(self, old_url, new_url): 120 """Decide whether Authorization header should be removed when redirecting""" 121 old_parsed = urlparse(old_url) 122 new_parsed = urlparse(new_url) 123 if old_parsed.hostname != new_parsed.hostname: 124 return True 125 # Special case: allow http -> https redirect when using the standard 126 # ports. This isn't specified by RFC 7235, but is kept to avoid 127 # breaking backwards compatibility with older versions of requests 128 # that allowed any redirects on the same host. 129 if (old_parsed.scheme == 'http' and old_parsed.port in (80, None) 130 and new_parsed.scheme == 'https' and new_parsed.port in (443, None)): 131 return False 132 133 # Handle default port usage corresponding to scheme. 134 changed_port = old_parsed.port != new_parsed.port 135 changed_scheme = old_parsed.scheme != new_parsed.scheme 136 default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None) 137 if (not changed_scheme and old_parsed.port in default_port 138 and new_parsed.port in default_port): 139 return False 140 141 # Standard case: root URI must match 142 return changed_port or changed_scheme 143 144 def resolve_redirects(self, resp, req, stream=False, timeout=None, 145 verify=True, cert=None, proxies=None, yield_requests=False, **adapter_kwargs): 146 """Receives a Response. Returns a generator of Responses or Requests.""" 147 148 hist = [] # keep track of history 149 150 url = self.get_redirect_target(resp) 151 previous_fragment = urlparse(req.url).fragment 152 while url: 153 prepared_request = req.copy() 154 155 # Update history and keep track of redirects. 156 # resp.history must ignore the original request in this loop 157 hist.append(resp) 158 resp.history = hist[1:] 159 160 try: 161 resp.content # Consume socket so it can be released 162 except (ChunkedEncodingError, ContentDecodingError, RuntimeError): 163 resp.raw.read(decode_content=False) 164 165 if len(resp.history) >= self.max_redirects: 166 raise TooManyRedirects('Exceeded {} redirects.'.format(self.max_redirects), response=resp) 167 168 # Release the connection back into the pool. 169 resp.close() 170 171 # Handle redirection without scheme (see: RFC 1808 Section 4) 172 if url.startswith('//'): 173 parsed_rurl = urlparse(resp.url) 174 url = ':'.join([to_native_string(parsed_rurl.scheme), url]) 175 176 # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2) 177 parsed = urlparse(url) 178 if parsed.fragment == '' and previous_fragment: 179 parsed = parsed._replace(fragment=previous_fragment) 180 elif parsed.fragment: 181 previous_fragment = parsed.fragment 182 url = parsed.geturl() 183 184 # Facilitate relative 'location' headers, as allowed by RFC 7231. 185 # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource') 186 # Compliant with RFC3986, we percent encode the url. 187 if not parsed.netloc: 188 url = urljoin(resp.url, requote_uri(url)) 189 else: 190 url = requote_uri(url) 191 192 prepared_request.url = to_native_string(url) 193 194 self.rebuild_method(prepared_request, resp) 195 196 # https://github.com/psf/requests/issues/1084 197 if resp.status_code not in (codes.temporary_redirect, codes.permanent_redirect): 198 # https://github.com/psf/requests/issues/3490 199 purged_headers = ('Content-Length', 'Content-Type', 'Transfer-Encoding') 200 for header in purged_headers: 201 prepared_request.headers.pop(header, None) 202 prepared_request.body = None 203 204 headers = prepared_request.headers 205 headers.pop('Cookie', None) 206 207 # Extract any cookies sent on the response to the cookiejar 208 # in the new request. Because we've mutated our copied prepared 209 # request, use the old one that we haven't yet touched. 210 extract_cookies_to_jar(prepared_request._cookies, req, resp.raw) 211 merge_cookies(prepared_request._cookies, self.cookies) 212 prepared_request.prepare_cookies(prepared_request._cookies) 213 214 # Rebuild auth and proxy information. 215 proxies = self.rebuild_proxies(prepared_request, proxies) 216 self.rebuild_auth(prepared_request, resp) 217 218 # A failed tell() sets `_body_position` to `object()`. This non-None 219 # value ensures `rewindable` will be True, allowing us to raise an 220 # UnrewindableBodyError, instead of hanging the connection. 221 rewindable = ( 222 prepared_request._body_position is not None and 223 ('Content-Length' in headers or 'Transfer-Encoding' in headers) 224 ) 225 226 # Attempt to rewind consumed file-like object. 227 if rewindable: 228 rewind_body(prepared_request) 229 230 # Override the original request. 231 req = prepared_request 232 233 if yield_requests: 234 yield req 235 else: 236 237 resp = self.send( 238 req, 239 stream=stream, 240 timeout=timeout, 241 verify=verify, 242 cert=cert, 243 proxies=proxies, 244 allow_redirects=False, 245 **adapter_kwargs 246 ) 247 248 extract_cookies_to_jar(self.cookies, prepared_request, resp.raw) 249 250 # extract redirect url, if any, for the next loop 251 url = self.get_redirect_target(resp) 252 yield resp 253 254 def rebuild_auth(self, prepared_request, response): 255 """When being redirected we may want to strip authentication from the 256 request to avoid leaking credentials. This method intelligently removes 257 and reapplies authentication where possible to avoid credential loss. 258 """ 259 headers = prepared_request.headers 260 url = prepared_request.url 261 262 if 'Authorization' in headers and self.should_strip_auth(response.request.url, url): 263 # If we get redirected to a new host, we should strip out any 264 # authentication headers. 265 del headers['Authorization'] 266 267 # .netrc might have more auth for us on our new host. 268 new_auth = get_netrc_auth(url) if self.trust_env else None 269 if new_auth is not None: 270 prepared_request.prepare_auth(new_auth) 271 272 273 def rebuild_proxies(self, prepared_request, proxies): 274 """This method re-evaluates the proxy configuration by considering the 275 environment variables. If we are redirected to a URL covered by 276 NO_PROXY, we strip the proxy configuration. Otherwise, we set missing 277 proxy keys for this URL (in case they were stripped by a previous 278 redirect). 279 280 This method also replaces the Proxy-Authorization header where 281 necessary. 282 283 :rtype: dict 284 """ 285 proxies = proxies if proxies is not None else {} 286 headers = prepared_request.headers 287 url = prepared_request.url 288 scheme = urlparse(url).scheme 289 new_proxies = proxies.copy() 290 no_proxy = proxies.get('no_proxy') 291 292 bypass_proxy = should_bypass_proxies(url, no_proxy=no_proxy) 293 if self.trust_env and not bypass_proxy: 294 environ_proxies = get_environ_proxies(url, no_proxy=no_proxy) 295 296 proxy = environ_proxies.get(scheme, environ_proxies.get('all')) 297 298 if proxy: 299 new_proxies.setdefault(scheme, proxy) 300 301 if 'Proxy-Authorization' in headers: 302 del headers['Proxy-Authorization'] 303 304 try: 305 username, password = get_auth_from_url(new_proxies[scheme]) 306 except KeyError: 307 username, password = None, None 308 309 if username and password: 310 headers['Proxy-Authorization'] = _basic_auth_str(username, password) 311 312 return new_proxies 313 314 def rebuild_method(self, prepared_request, response): 315 """When being redirected we may want to change the method of the request 316 based on certain specs or browser behavior. 317 """ 318 method = prepared_request.method 319 320 # https://tools.ietf.org/html/rfc7231#section-6.4.4 321 if response.status_code == codes.see_other and method != 'HEAD': 322 method = 'GET' 323 324 # Do what the browsers do, despite standards... 325 # First, turn 302s into GETs. 326 if response.status_code == codes.found and method != 'HEAD': 327 method = 'GET' 328 329 # Second, if a POST is responded to with a 301, turn it into a GET. 330 # This bizarre behaviour is explained in Issue 1704. 331 if response.status_code == codes.moved and method == 'POST': 332 method = 'GET' 333 334 prepared_request.method = method 335 336 337class Session(SessionRedirectMixin): 338 """A Requests session. 339 340 Provides cookie persistence, connection-pooling, and configuration. 341 342 Basic Usage:: 343 344 >>> import requests 345 >>> s = requests.Session() 346 >>> s.get('https://httpbin.org/get') 347 <Response [200]> 348 349 Or as a context manager:: 350 351 >>> with requests.Session() as s: 352 ... s.get('https://httpbin.org/get') 353 <Response [200]> 354 """ 355 356 __attrs__ = [ 357 'headers', 'cookies', 'auth', 'proxies', 'hooks', 'params', 'verify', 358 'cert', 'adapters', 'stream', 'trust_env', 359 'max_redirects', 360 ] 361 362 def __init__(self): 363 364 #: A case-insensitive dictionary of headers to be sent on each 365 #: :class:`Request <Request>` sent from this 366 #: :class:`Session <Session>`. 367 self.headers = default_headers() 368 369 #: Default Authentication tuple or object to attach to 370 #: :class:`Request <Request>`. 371 self.auth = None 372 373 #: Dictionary mapping protocol or protocol and host to the URL of the proxy 374 #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to 375 #: be used on each :class:`Request <Request>`. 376 self.proxies = {} 377 378 #: Event-handling hooks. 379 self.hooks = default_hooks() 380 381 #: Dictionary of querystring data to attach to each 382 #: :class:`Request <Request>`. The dictionary values may be lists for 383 #: representing multivalued query parameters. 384 self.params = {} 385 386 #: Stream response content default. 387 self.stream = False 388 389 #: SSL Verification default. 390 #: Defaults to `True`, requiring requests to verify the TLS certificate at the 391 #: remote end. 392 #: If verify is set to `False`, requests will accept any TLS certificate 393 #: presented by the server, and will ignore hostname mismatches and/or 394 #: expired certificates, which will make your application vulnerable to 395 #: man-in-the-middle (MitM) attacks. 396 #: Only set this to `False` for testing. 397 self.verify = True 398 399 #: SSL client certificate default, if String, path to ssl client 400 #: cert file (.pem). If Tuple, ('cert', 'key') pair. 401 self.cert = None 402 403 #: Maximum number of redirects allowed. If the request exceeds this 404 #: limit, a :class:`TooManyRedirects` exception is raised. 405 #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is 406 #: 30. 407 self.max_redirects = DEFAULT_REDIRECT_LIMIT 408 409 #: Trust environment settings for proxy configuration, default 410 #: authentication and similar. 411 self.trust_env = True 412 413 #: A CookieJar containing all currently outstanding cookies set on this 414 #: session. By default it is a 415 #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but 416 #: may be any other ``cookielib.CookieJar`` compatible object. 417 self.cookies = cookiejar_from_dict({}) 418 419 # Default connection adapters. 420 self.adapters = OrderedDict() 421 self.mount('https://', HTTPAdapter()) 422 self.mount('http://', HTTPAdapter()) 423 424 def __enter__(self): 425 return self 426 427 def __exit__(self, *args): 428 self.close() 429 430 def prepare_request(self, request): 431 """Constructs a :class:`PreparedRequest <PreparedRequest>` for 432 transmission and returns it. The :class:`PreparedRequest` has settings 433 merged from the :class:`Request <Request>` instance and those of the 434 :class:`Session`. 435 436 :param request: :class:`Request` instance to prepare with this 437 session's settings. 438 :rtype: requests.PreparedRequest 439 """ 440 cookies = request.cookies or {} 441 442 # Bootstrap CookieJar. 443 if not isinstance(cookies, cookielib.CookieJar): 444 cookies = cookiejar_from_dict(cookies) 445 446 # Merge with session cookies 447 merged_cookies = merge_cookies( 448 merge_cookies(RequestsCookieJar(), self.cookies), cookies) 449 450 # Set environment's basic authentication if not explicitly set. 451 auth = request.auth 452 if self.trust_env and not auth and not self.auth: 453 auth = get_netrc_auth(request.url) 454 455 p = PreparedRequest() 456 p.prepare( 457 method=request.method.upper(), 458 url=request.url, 459 files=request.files, 460 data=request.data, 461 json=request.json, 462 headers=merge_setting(request.headers, self.headers, dict_class=CaseInsensitiveDict), 463 params=merge_setting(request.params, self.params), 464 auth=merge_setting(auth, self.auth), 465 cookies=merged_cookies, 466 hooks=merge_hooks(request.hooks, self.hooks), 467 ) 468 return p 469 470 def request(self, method, url, 471 params=None, data=None, headers=None, cookies=None, files=None, 472 auth=None, timeout=None, allow_redirects=True, proxies=None, 473 hooks=None, stream=None, verify=None, cert=None, json=None): 474 """Constructs a :class:`Request <Request>`, prepares it and sends it. 475 Returns :class:`Response <Response>` object. 476 477 :param method: method for the new :class:`Request` object. 478 :param url: URL for the new :class:`Request` object. 479 :param params: (optional) Dictionary or bytes to be sent in the query 480 string for the :class:`Request`. 481 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 482 object to send in the body of the :class:`Request`. 483 :param json: (optional) json to send in the body of the 484 :class:`Request`. 485 :param headers: (optional) Dictionary of HTTP Headers to send with the 486 :class:`Request`. 487 :param cookies: (optional) Dict or CookieJar object to send with the 488 :class:`Request`. 489 :param files: (optional) Dictionary of ``'filename': file-like-objects`` 490 for multipart encoding upload. 491 :param auth: (optional) Auth tuple or callable to enable 492 Basic/Digest/Custom HTTP Auth. 493 :param timeout: (optional) How long to wait for the server to send 494 data before giving up, as a float, or a :ref:`(connect timeout, 495 read timeout) <timeouts>` tuple. 496 :type timeout: float or tuple 497 :param allow_redirects: (optional) Set to True by default. 498 :type allow_redirects: bool 499 :param proxies: (optional) Dictionary mapping protocol or protocol and 500 hostname to the URL of the proxy. 501 :param stream: (optional) whether to immediately download the response 502 content. Defaults to ``False``. 503 :param verify: (optional) Either a boolean, in which case it controls whether we verify 504 the server's TLS certificate, or a string, in which case it must be a path 505 to a CA bundle to use. Defaults to ``True``. When set to 506 ``False``, requests will accept any TLS certificate presented by 507 the server, and will ignore hostname mismatches and/or expired 508 certificates, which will make your application vulnerable to 509 man-in-the-middle (MitM) attacks. Setting verify to ``False`` 510 may be useful during local development or testing. 511 :param cert: (optional) if String, path to ssl client cert file (.pem). 512 If Tuple, ('cert', 'key') pair. 513 :rtype: requests.Response 514 """ 515 # Create the Request. 516 req = Request( 517 method=method.upper(), 518 url=url, 519 headers=headers, 520 files=files, 521 data=data or {}, 522 json=json, 523 params=params or {}, 524 auth=auth, 525 cookies=cookies, 526 hooks=hooks, 527 ) 528 prep = self.prepare_request(req) 529 530 proxies = proxies or {} 531 532 settings = self.merge_environment_settings( 533 prep.url, proxies, stream, verify, cert 534 ) 535 536 # Send the request. 537 send_kwargs = { 538 'timeout': timeout, 539 'allow_redirects': allow_redirects, 540 } 541 send_kwargs.update(settings) 542 resp = self.send(prep, **send_kwargs) 543 544 return resp 545 546 def get(self, url, **kwargs): 547 r"""Sends a GET request. Returns :class:`Response` object. 548 549 :param url: URL for the new :class:`Request` object. 550 :param \*\*kwargs: Optional arguments that ``request`` takes. 551 :rtype: requests.Response 552 """ 553 554 kwargs.setdefault('allow_redirects', True) 555 return self.request('GET', url, **kwargs) 556 557 def options(self, url, **kwargs): 558 r"""Sends a OPTIONS request. Returns :class:`Response` object. 559 560 :param url: URL for the new :class:`Request` object. 561 :param \*\*kwargs: Optional arguments that ``request`` takes. 562 :rtype: requests.Response 563 """ 564 565 kwargs.setdefault('allow_redirects', True) 566 return self.request('OPTIONS', url, **kwargs) 567 568 def head(self, url, **kwargs): 569 r"""Sends a HEAD request. Returns :class:`Response` object. 570 571 :param url: URL for the new :class:`Request` object. 572 :param \*\*kwargs: Optional arguments that ``request`` takes. 573 :rtype: requests.Response 574 """ 575 576 kwargs.setdefault('allow_redirects', False) 577 return self.request('HEAD', url, **kwargs) 578 579 def post(self, url, data=None, json=None, **kwargs): 580 r"""Sends a POST request. Returns :class:`Response` object. 581 582 :param url: URL for the new :class:`Request` object. 583 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 584 object to send in the body of the :class:`Request`. 585 :param json: (optional) json to send in the body of the :class:`Request`. 586 :param \*\*kwargs: Optional arguments that ``request`` takes. 587 :rtype: requests.Response 588 """ 589 590 return self.request('POST', url, data=data, json=json, **kwargs) 591 592 def put(self, url, data=None, **kwargs): 593 r"""Sends a PUT request. Returns :class:`Response` object. 594 595 :param url: URL for the new :class:`Request` object. 596 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 597 object to send in the body of the :class:`Request`. 598 :param \*\*kwargs: Optional arguments that ``request`` takes. 599 :rtype: requests.Response 600 """ 601 602 return self.request('PUT', url, data=data, **kwargs) 603 604 def patch(self, url, data=None, **kwargs): 605 r"""Sends a PATCH request. Returns :class:`Response` object. 606 607 :param url: URL for the new :class:`Request` object. 608 :param data: (optional) Dictionary, list of tuples, bytes, or file-like 609 object to send in the body of the :class:`Request`. 610 :param \*\*kwargs: Optional arguments that ``request`` takes. 611 :rtype: requests.Response 612 """ 613 614 return self.request('PATCH', url, data=data, **kwargs) 615 616 def delete(self, url, **kwargs): 617 r"""Sends a DELETE request. Returns :class:`Response` object. 618 619 :param url: URL for the new :class:`Request` object. 620 :param \*\*kwargs: Optional arguments that ``request`` takes. 621 :rtype: requests.Response 622 """ 623 624 return self.request('DELETE', url, **kwargs) 625 626 def send(self, request, **kwargs): 627 """Send a given PreparedRequest. 628 629 :rtype: requests.Response 630 """ 631 # Set defaults that the hooks can utilize to ensure they always have 632 # the correct parameters to reproduce the previous request. 633 kwargs.setdefault('stream', self.stream) 634 kwargs.setdefault('verify', self.verify) 635 kwargs.setdefault('cert', self.cert) 636 kwargs.setdefault('proxies', self.proxies) 637 638 # It's possible that users might accidentally send a Request object. 639 # Guard against that specific failure case. 640 if isinstance(request, Request): 641 raise ValueError('You can only send PreparedRequests.') 642 643 # Set up variables needed for resolve_redirects and dispatching of hooks 644 allow_redirects = kwargs.pop('allow_redirects', True) 645 stream = kwargs.get('stream') 646 hooks = request.hooks 647 648 # Get the appropriate adapter to use 649 adapter = self.get_adapter(url=request.url) 650 651 # Start time (approximately) of the request 652 start = preferred_clock() 653 654 # Send the request 655 r = adapter.send(request, **kwargs) 656 657 # Total elapsed time of the request (approximately) 658 elapsed = preferred_clock() - start 659 r.elapsed = timedelta(seconds=elapsed) 660 661 # Response manipulation hooks 662 r = dispatch_hook('response', hooks, r, **kwargs) 663 664 # Persist cookies 665 if r.history: 666 667 # If the hooks create history then we want those cookies too 668 for resp in r.history: 669 extract_cookies_to_jar(self.cookies, resp.request, resp.raw) 670 671 extract_cookies_to_jar(self.cookies, request, r.raw) 672 673 # Resolve redirects if allowed. 674 if allow_redirects: 675 # Redirect resolving generator. 676 gen = self.resolve_redirects(r, request, **kwargs) 677 history = [resp for resp in gen] 678 else: 679 history = [] 680 681 # Shuffle things around if there's history. 682 if history: 683 # Insert the first (original) request at the start 684 history.insert(0, r) 685 # Get the last request made 686 r = history.pop() 687 r.history = history 688 689 # If redirects aren't being followed, store the response on the Request for Response.next(). 690 if not allow_redirects: 691 try: 692 r._next = next(self.resolve_redirects(r, request, yield_requests=True, **kwargs)) 693 except StopIteration: 694 pass 695 696 if not stream: 697 r.content 698 699 return r 700 701 def merge_environment_settings(self, url, proxies, stream, verify, cert): 702 """ 703 Check the environment and merge it with some settings. 704 705 :rtype: dict 706 """ 707 # Gather clues from the surrounding environment. 708 if self.trust_env: 709 # Set environment's proxies. 710 no_proxy = proxies.get('no_proxy') if proxies is not None else None 711 env_proxies = get_environ_proxies(url, no_proxy=no_proxy) 712 for (k, v) in env_proxies.items(): 713 proxies.setdefault(k, v) 714 715 # Look for requests environment configuration and be compatible 716 # with cURL. 717 if verify is True or verify is None: 718 verify = (os.environ.get('REQUESTS_CA_BUNDLE') or 719 os.environ.get('CURL_CA_BUNDLE')) 720 721 # Merge all the kwargs. 722 proxies = merge_setting(proxies, self.proxies) 723 stream = merge_setting(stream, self.stream) 724 verify = merge_setting(verify, self.verify) 725 cert = merge_setting(cert, self.cert) 726 727 return {'verify': verify, 'proxies': proxies, 'stream': stream, 728 'cert': cert} 729 730 def get_adapter(self, url): 731 """ 732 Returns the appropriate connection adapter for the given URL. 733 734 :rtype: requests.adapters.BaseAdapter 735 """ 736 for (prefix, adapter) in self.adapters.items(): 737 738 if url.lower().startswith(prefix.lower()): 739 return adapter 740 741 # Nothing matches :-/ 742 raise InvalidSchema("No connection adapters were found for {!r}".format(url)) 743 744 def close(self): 745 """Closes all adapters and as such the session""" 746 for v in self.adapters.values(): 747 v.close() 748 749 def mount(self, prefix, adapter): 750 """Registers a connection adapter to a prefix. 751 752 Adapters are sorted in descending order by prefix length. 753 """ 754 self.adapters[prefix] = adapter 755 keys_to_move = [k for k in self.adapters if len(k) < len(prefix)] 756 757 for key in keys_to_move: 758 self.adapters[key] = self.adapters.pop(key) 759 760 def __getstate__(self): 761 state = {attr: getattr(self, attr, None) for attr in self.__attrs__} 762 return state 763 764 def __setstate__(self, state): 765 for attr, value in state.items(): 766 setattr(self, attr, value) 767 768 769def session(): 770 """ 771 Returns a :class:`Session` for context-management. 772 773 .. deprecated:: 1.0.0 774 775 This method has been deprecated since version 1.0.0 and is only kept for 776 backwards compatibility. New code should use :class:`~requests.sessions.Session` 777 to create a session. This may be removed at a future date. 778 779 :rtype: Session 780 """ 781 return Session() 782