comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/python3.8/site-packages/requests/sessions.py @ 68:5028fdace37b

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