python (3.11.7)
       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 ESC[4;38;5;81mSessionRedirectMixin:
     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  
     266                  resp = self.send(
     267                      req,
     268                      stream=stream,
     269                      timeout=timeout,
     270                      verify=verify,
     271                      cert=cert,
     272                      proxies=proxies,
     273                      allow_redirects=False,
     274                      **adapter_kwargs,
     275                  )
     276  
     277                  extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)
     278  
     279                  # extract redirect url, if any, for the next loop
     280                  url = self.get_redirect_target(resp)
     281                  yield resp
     282  
     283      def rebuild_auth(self, prepared_request, response):
     284          """When being redirected we may want to strip authentication from the
     285          request to avoid leaking credentials. This method intelligently removes
     286          and reapplies authentication where possible to avoid credential loss.
     287          """
     288          headers = prepared_request.headers
     289          url = prepared_request.url
     290  
     291          if "Authorization" in headers and self.should_strip_auth(
     292              response.request.url, url
     293          ):
     294              # If we get redirected to a new host, we should strip out any
     295              # authentication headers.
     296              del headers["Authorization"]
     297  
     298          # .netrc might have more auth for us on our new host.
     299          new_auth = get_netrc_auth(url) if self.trust_env else None
     300          if new_auth is not None:
     301              prepared_request.prepare_auth(new_auth)
     302  
     303      def rebuild_proxies(self, prepared_request, proxies):
     304          """This method re-evaluates the proxy configuration by considering the
     305          environment variables. If we are redirected to a URL covered by
     306          NO_PROXY, we strip the proxy configuration. Otherwise, we set missing
     307          proxy keys for this URL (in case they were stripped by a previous
     308          redirect).
     309  
     310          This method also replaces the Proxy-Authorization header where
     311          necessary.
     312  
     313          :rtype: dict
     314          """
     315          headers = prepared_request.headers
     316          scheme = urlparse(prepared_request.url).scheme
     317          new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)
     318  
     319          if "Proxy-Authorization" in headers:
     320              del headers["Proxy-Authorization"]
     321  
     322          try:
     323              username, password = get_auth_from_url(new_proxies[scheme])
     324          except KeyError:
     325              username, password = None, None
     326  
     327          # urllib3 handles proxy authorization for us in the standard adapter.
     328          # Avoid appending this to TLS tunneled requests where it may be leaked.
     329          if not scheme.startswith('https') and username and password:
     330              headers["Proxy-Authorization"] = _basic_auth_str(username, password)
     331  
     332          return new_proxies
     333  
     334      def rebuild_method(self, prepared_request, response):
     335          """When being redirected we may want to change the method of the request
     336          based on certain specs or browser behavior.
     337          """
     338          method = prepared_request.method
     339  
     340          # https://tools.ietf.org/html/rfc7231#section-6.4.4
     341          if response.status_code == codes.see_other and method != "HEAD":
     342              method = "GET"
     343  
     344          # Do what the browsers do, despite standards...
     345          # First, turn 302s into GETs.
     346          if response.status_code == codes.found and method != "HEAD":
     347              method = "GET"
     348  
     349          # Second, if a POST is responded to with a 301, turn it into a GET.
     350          # This bizarre behaviour is explained in Issue 1704.
     351          if response.status_code == codes.moved and method == "POST":
     352              method = "GET"
     353  
     354          prepared_request.method = method
     355  
     356  
     357  class ESC[4;38;5;81mSession(ESC[4;38;5;149mSessionRedirectMixin):
     358      """A Requests session.
     359  
     360      Provides cookie persistence, connection-pooling, and configuration.
     361  
     362      Basic Usage::
     363  
     364        >>> import requests
     365        >>> s = requests.Session()
     366        >>> s.get('https://httpbin.org/get')
     367        <Response [200]>
     368  
     369      Or as a context manager::
     370  
     371        >>> with requests.Session() as s:
     372        ...     s.get('https://httpbin.org/get')
     373        <Response [200]>
     374      """
     375  
     376      __attrs__ = [
     377          "headers",
     378          "cookies",
     379          "auth",
     380          "proxies",
     381          "hooks",
     382          "params",
     383          "verify",
     384          "cert",
     385          "adapters",
     386          "stream",
     387          "trust_env",
     388          "max_redirects",
     389      ]
     390  
     391      def __init__(self):
     392  
     393          #: A case-insensitive dictionary of headers to be sent on each
     394          #: :class:`Request <Request>` sent from this
     395          #: :class:`Session <Session>`.
     396          self.headers = default_headers()
     397  
     398          #: Default Authentication tuple or object to attach to
     399          #: :class:`Request <Request>`.
     400          self.auth = None
     401  
     402          #: Dictionary mapping protocol or protocol and host to the URL of the proxy
     403          #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to
     404          #: be used on each :class:`Request <Request>`.
     405          self.proxies = {}
     406  
     407          #: Event-handling hooks.
     408          self.hooks = default_hooks()
     409  
     410          #: Dictionary of querystring data to attach to each
     411          #: :class:`Request <Request>`. The dictionary values may be lists for
     412          #: representing multivalued query parameters.
     413          self.params = {}
     414  
     415          #: Stream response content default.
     416          self.stream = False
     417  
     418          #: SSL Verification default.
     419          #: Defaults to `True`, requiring requests to verify the TLS certificate at the
     420          #: remote end.
     421          #: If verify is set to `False`, requests will accept any TLS certificate
     422          #: presented by the server, and will ignore hostname mismatches and/or
     423          #: expired certificates, which will make your application vulnerable to
     424          #: man-in-the-middle (MitM) attacks.
     425          #: Only set this to `False` for testing.
     426          self.verify = True
     427  
     428          #: SSL client certificate default, if String, path to ssl client
     429          #: cert file (.pem). If Tuple, ('cert', 'key') pair.
     430          self.cert = None
     431  
     432          #: Maximum number of redirects allowed. If the request exceeds this
     433          #: limit, a :class:`TooManyRedirects` exception is raised.
     434          #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is
     435          #: 30.
     436          self.max_redirects = DEFAULT_REDIRECT_LIMIT
     437  
     438          #: Trust environment settings for proxy configuration, default
     439          #: authentication and similar.
     440          self.trust_env = True
     441  
     442          #: A CookieJar containing all currently outstanding cookies set on this
     443          #: session. By default it is a
     444          #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but
     445          #: may be any other ``cookielib.CookieJar`` compatible object.
     446          self.cookies = cookiejar_from_dict({})
     447  
     448          # Default connection adapters.
     449          self.adapters = OrderedDict()
     450          self.mount("https://", HTTPAdapter())
     451          self.mount("http://", HTTPAdapter())
     452  
     453      def __enter__(self):
     454          return self
     455  
     456      def __exit__(self, *args):
     457          self.close()
     458  
     459      def prepare_request(self, request):
     460          """Constructs a :class:`PreparedRequest <PreparedRequest>` for
     461          transmission and returns it. The :class:`PreparedRequest` has settings
     462          merged from the :class:`Request <Request>` instance and those of the
     463          :class:`Session`.
     464  
     465          :param request: :class:`Request` instance to prepare with this
     466              session's settings.
     467          :rtype: requests.PreparedRequest
     468          """
     469          cookies = request.cookies or {}
     470  
     471          # Bootstrap CookieJar.
     472          if not isinstance(cookies, cookielib.CookieJar):
     473              cookies = cookiejar_from_dict(cookies)
     474  
     475          # Merge with session cookies
     476          merged_cookies = merge_cookies(
     477              merge_cookies(RequestsCookieJar(), self.cookies), cookies
     478          )
     479  
     480          # Set environment's basic authentication if not explicitly set.
     481          auth = request.auth
     482          if self.trust_env and not auth and not self.auth:
     483              auth = get_netrc_auth(request.url)
     484  
     485          p = PreparedRequest()
     486          p.prepare(
     487              method=request.method.upper(),
     488              url=request.url,
     489              files=request.files,
     490              data=request.data,
     491              json=request.json,
     492              headers=merge_setting(
     493                  request.headers, self.headers, dict_class=CaseInsensitiveDict
     494              ),
     495              params=merge_setting(request.params, self.params),
     496              auth=merge_setting(auth, self.auth),
     497              cookies=merged_cookies,
     498              hooks=merge_hooks(request.hooks, self.hooks),
     499          )
     500          return p
     501  
     502      def request(
     503          self,
     504          method,
     505          url,
     506          params=None,
     507          data=None,
     508          headers=None,
     509          cookies=None,
     510          files=None,
     511          auth=None,
     512          timeout=None,
     513          allow_redirects=True,
     514          proxies=None,
     515          hooks=None,
     516          stream=None,
     517          verify=None,
     518          cert=None,
     519          json=None,
     520      ):
     521          """Constructs a :class:`Request <Request>`, prepares it and sends it.
     522          Returns :class:`Response <Response>` object.
     523  
     524          :param method: method for the new :class:`Request` object.
     525          :param url: URL for the new :class:`Request` object.
     526          :param params: (optional) Dictionary or bytes to be sent in the query
     527              string for the :class:`Request`.
     528          :param data: (optional) Dictionary, list of tuples, bytes, or file-like
     529              object to send in the body of the :class:`Request`.
     530          :param json: (optional) json to send in the body of the
     531              :class:`Request`.
     532          :param headers: (optional) Dictionary of HTTP Headers to send with the
     533              :class:`Request`.
     534          :param cookies: (optional) Dict or CookieJar object to send with the
     535              :class:`Request`.
     536          :param files: (optional) Dictionary of ``'filename': file-like-objects``
     537              for multipart encoding upload.
     538          :param auth: (optional) Auth tuple or callable to enable
     539              Basic/Digest/Custom HTTP Auth.
     540          :param timeout: (optional) How long to wait for the server to send
     541              data before giving up, as a float, or a :ref:`(connect timeout,
     542              read timeout) <timeouts>` tuple.
     543          :type timeout: float or tuple
     544          :param allow_redirects: (optional) Set to True by default.
     545          :type allow_redirects: bool
     546          :param proxies: (optional) Dictionary mapping protocol or protocol and
     547              hostname to the URL of the proxy.
     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  
     715              # If the hooks create history then we want those cookies too
     716              for resp in r.history:
     717                  extract_cookies_to_jar(self.cookies, resp.request, resp.raw)
     718  
     719          extract_cookies_to_jar(self.cookies, request, r.raw)
     720  
     721          # Resolve redirects if allowed.
     722          if allow_redirects:
     723              # Redirect resolving generator.
     724              gen = self.resolve_redirects(r, request, **kwargs)
     725              history = [resp for resp in gen]
     726          else:
     727              history = []
     728  
     729          # Shuffle things around if there's history.
     730          if history:
     731              # Insert the first (original) request at the start
     732              history.insert(0, r)
     733              # Get the last request made
     734              r = history.pop()
     735              r.history = history
     736  
     737          # If redirects aren't being followed, store the response on the Request for Response.next().
     738          if not allow_redirects:
     739              try:
     740                  r._next = next(
     741                      self.resolve_redirects(r, request, yield_requests=True, **kwargs)
     742                  )
     743              except StopIteration:
     744                  pass
     745  
     746          if not stream:
     747              r.content
     748  
     749          return r
     750  
     751      def merge_environment_settings(self, url, proxies, stream, verify, cert):
     752          """
     753          Check the environment and merge it with some settings.
     754  
     755          :rtype: dict
     756          """
     757          # Gather clues from the surrounding environment.
     758          if self.trust_env:
     759              # Set environment's proxies.
     760              no_proxy = proxies.get("no_proxy") if proxies is not None else None
     761              env_proxies = get_environ_proxies(url, no_proxy=no_proxy)
     762              for (k, v) in env_proxies.items():
     763                  proxies.setdefault(k, v)
     764  
     765              # Look for requests environment configuration
     766              # and be compatible with cURL.
     767              if verify is True or verify is None:
     768                  verify = (
     769                      os.environ.get("REQUESTS_CA_BUNDLE")
     770                      or os.environ.get("CURL_CA_BUNDLE")
     771                      or verify
     772                  )
     773  
     774          # Merge all the kwargs.
     775          proxies = merge_setting(proxies, self.proxies)
     776          stream = merge_setting(stream, self.stream)
     777          verify = merge_setting(verify, self.verify)
     778          cert = merge_setting(cert, self.cert)
     779  
     780          return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}
     781  
     782      def get_adapter(self, url):
     783          """
     784          Returns the appropriate connection adapter for the given URL.
     785  
     786          :rtype: requests.adapters.BaseAdapter
     787          """
     788          for (prefix, adapter) in self.adapters.items():
     789  
     790              if url.lower().startswith(prefix.lower()):
     791                  return adapter
     792  
     793          # Nothing matches :-/
     794          raise InvalidSchema(f"No connection adapters were found for {url!r}")
     795  
     796      def close(self):
     797          """Closes all adapters and as such the session"""
     798          for v in self.adapters.values():
     799              v.close()
     800  
     801      def mount(self, prefix, adapter):
     802          """Registers a connection adapter to a prefix.
     803  
     804          Adapters are sorted in descending order by prefix length.
     805          """
     806          self.adapters[prefix] = adapter
     807          keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]
     808  
     809          for key in keys_to_move:
     810              self.adapters[key] = self.adapters.pop(key)
     811  
     812      def __getstate__(self):
     813          state = {attr: getattr(self, attr, None) for attr in self.__attrs__}
     814          return state
     815  
     816      def __setstate__(self, state):
     817          for attr, value in state.items():
     818              setattr(self, attr, value)
     819  
     820  
     821  def session():
     822      """
     823      Returns a :class:`Session` for context-management.
     824  
     825      .. deprecated:: 1.0.0
     826  
     827          This method has been deprecated since version 1.0.0 and is only kept for
     828          backwards compatibility. New code should use :class:`~requests.sessions.Session`
     829          to create a session. This may be removed at a future date.
     830  
     831      :rtype: Session
     832      """
     833      return Session()