Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
sessions.py834 linesDownload Raw Back to requests
1"""2requests.sessions3~~~~~~~~~~~~~~~~~4 5This module provides a Session object to manage and persist settings across6requests (cookies, auth, proxies).7"""8import os9import sys10import time11from collections import OrderedDict12from datetime import timedelta13 14from ._internal_utils import to_native_string15from .adapters import HTTPAdapter16from .auth import _basic_auth_str17from .compat import Mapping, cookielib, urljoin, urlparse18from .cookies import (19    RequestsCookieJar,20    cookiejar_from_dict,21    extract_cookies_to_jar,22    merge_cookies,23)24from .exceptions import (25    ChunkedEncodingError,26    ContentDecodingError,27    InvalidSchema,28    TooManyRedirects,29)30from .hooks import default_hooks, dispatch_hook31 32# formerly defined here, reexposed here for backward compatibility33from .models import (  # noqa: F40134    DEFAULT_REDIRECT_LIMIT,35    REDIRECT_STATI,36    PreparedRequest,37    Request,38)39from .status_codes import codes40from .structures import CaseInsensitiveDict41from .utils import (  # noqa: F40142    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.55if sys.platform == "win32":56    preferred_clock = time.perf_counter57else:58    preferred_clock = time.time59 60 61def merge_setting(request_setting, session_setting, dict_class=OrderedDict):62    """Determines appropriate setting for a given request, taking into account63    the explicit setting on that request, and the setting in the session. If a64    setting is a dictionary, they will be merged together using `dict_class`65    """66 67    if session_setting is None:68        return request_setting69 70    if request_setting is None:71        return session_setting72 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_setting78 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 altering83    # 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_setting89 90 91def 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': []}, the95    merge breaks Session hooks entirely.96    """97    if session_hooks is None or session_hooks.get("response") == []:98        return request_hooks99 100    if request_hooks is None or request_hooks.get("response") == []:101        return session_hooks102 103    return merge_setting(request_hooks, session_hooks, dict_class)104 105 106class 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 will110        # be called at least once upon the original response and at least twice111        # on each subsequent redirect response (if any).112        # If a custom mixin is used to handle this logic, it may be advantageous113        # to cache the redirect location onto the response object as a private114        # attribute.115        if resp.is_redirect:116            location = resp.headers["location"]117            # Currently the underlying http module on py3 decode headers118            # in latin1, but empirical evidence suggests that latin1 is very119            # 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 None126 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 True133        # Special case: allow http -> https redirect when using the standard134        # ports. This isn't specified by RFC 7235, but is kept to avoid135        # breaking backwards compatibility with older versions of requests136        # 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 False144 145        # Handle default port usage corresponding to scheme.146        changed_port = old_parsed.port != new_parsed.port147        changed_scheme = old_parsed.scheme != new_parsed.scheme148        default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)149        if (150            not changed_scheme151            and old_parsed.port in default_port152            and new_parsed.port in default_port153        ):154            return False155 156        # Standard case: root URI must match157        return changed_port or changed_scheme158 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 history174 175        url = self.get_redirect_target(resp)176        previous_fragment = urlparse(req.url).fragment177        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 loop182            hist.append(resp)183            resp.history = hist[1:]184 185            try:186                resp.content  # Consume socket so it can be released187            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=resp193                )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.fragment209            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/1084224            if resp.status_code not in (225                codes.temporary_redirect,226                codes.permanent_redirect,227            ):228                # https://github.com/psf/requests/issues/3490229                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 = None233 234            headers = prepared_request.headers235            headers.pop("Cookie", None)236 237            # Extract any cookies sent on the response to the cookiejar238            # in the new request. Because we've mutated our copied prepared239            # 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-None249            # value ensures `rewindable` will be True, allowing us to raise an250            # 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 headers253            )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_request261 262            if yield_requests:263                yield req264            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 loop280                url = self.get_redirect_target(resp)281                yield resp282 283    def rebuild_auth(self, prepared_request, response):284        """When being redirected we may want to strip authentication from the285        request to avoid leaking credentials. This method intelligently removes286        and reapplies authentication where possible to avoid credential loss.287        """288        headers = prepared_request.headers289        url = prepared_request.url290 291        if "Authorization" in headers and self.should_strip_auth(292            response.request.url, url293        ):294            # If we get redirected to a new host, we should strip out any295            # 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 None300        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 the305        environment variables. If we are redirected to a URL covered by306        NO_PROXY, we strip the proxy configuration. Otherwise, we set missing307        proxy keys for this URL (in case they were stripped by a previous308        redirect).309 310        This method also replaces the Proxy-Authorization header where311        necessary.312 313        :rtype: dict314        """315        headers = prepared_request.headers316        scheme = urlparse(prepared_request.url).scheme317        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, None326 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_proxies333 334    def rebuild_method(self, prepared_request, response):335        """When being redirected we may want to change the method of the request336        based on certain specs or browser behavior.337        """338        method = prepared_request.method339 340        # https://tools.ietf.org/html/rfc7231#section-6.4.4341        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 = method355 356 357class Session(SessionRedirectMixin):358    """A Requests session.359 360    Provides cookie persistence, connection-pooling, and configuration.361 362    Basic Usage::363 364      >>> import requests365      >>> 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 each394        #: :class:`Request <Request>` sent from this395        #: :class:`Session <Session>`.396        self.headers = default_headers()397 398        #: Default Authentication tuple or object to attach to399        #: :class:`Request <Request>`.400        self.auth = None401 402        #: Dictionary mapping protocol or protocol and host to the URL of the proxy403        #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to404        #: 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 each411        #: :class:`Request <Request>`. The dictionary values may be lists for412        #: representing multivalued query parameters.413        self.params = {}414 415        #: Stream response content default.416        self.stream = False417 418        #: SSL Verification default.419        #: Defaults to `True`, requiring requests to verify the TLS certificate at the420        #: remote end.421        #: If verify is set to `False`, requests will accept any TLS certificate422        #: presented by the server, and will ignore hostname mismatches and/or423        #: expired certificates, which will make your application vulnerable to424        #: man-in-the-middle (MitM) attacks.425        #: Only set this to `False` for testing.426        self.verify = True427 428        #: SSL client certificate default, if String, path to ssl client429        #: cert file (.pem). If Tuple, ('cert', 'key') pair.430        self.cert = None431 432        #: Maximum number of redirects allowed. If the request exceeds this433        #: limit, a :class:`TooManyRedirects` exception is raised.434        #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is435        #: 30.436        self.max_redirects = DEFAULT_REDIRECT_LIMIT437 438        #: Trust environment settings for proxy configuration, default439        #: authentication and similar.440        self.trust_env = True441 442        #: A CookieJar containing all currently outstanding cookies set on this443        #: session. By default it is a444        #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but445        #: 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 self455 456    def __exit__(self, *args):457        self.close()458 459    def prepare_request(self, request):460        """Constructs a :class:`PreparedRequest <PreparedRequest>` for461        transmission and returns it. The :class:`PreparedRequest` has settings462        merged from the :class:`Request <Request>` instance and those of the463        :class:`Session`.464 465        :param request: :class:`Request` instance to prepare with this466            session's settings.467        :rtype: requests.PreparedRequest468        """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 cookies476        merged_cookies = merge_cookies(477            merge_cookies(RequestsCookieJar(), self.cookies), cookies478        )479 480        # Set environment's basic authentication if not explicitly set.481        auth = request.auth482        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=CaseInsensitiveDict494            ),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 p501 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 query527            string for the :class:`Request`.528        :param data: (optional) Dictionary, list of tuples, bytes, or file-like529            object to send in the body of the :class:`Request`.530        :param json: (optional) json to send in the body of the531            :class:`Request`.532        :param headers: (optional) Dictionary of HTTP Headers to send with the533            :class:`Request`.534        :param cookies: (optional) Dict or CookieJar object to send with the535            :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 enable539            Basic/Digest/Custom HTTP Auth.540        :param timeout: (optional) How long to wait for the server to send541            data before giving up, as a float, or a :ref:`(connect timeout,542            read timeout) <timeouts>` tuple.543        :type timeout: float or tuple544        :param allow_redirects: (optional) Set to True by default.545        :type allow_redirects: bool546        :param proxies: (optional) Dictionary mapping protocol or protocol and547            hostname to the URL of the proxy.548        :param stream: (optional) whether to immediately download the response549            content. Defaults to ``False``.550        :param verify: (optional) Either a boolean, in which case it controls whether we verify551            the server's TLS certificate, or a string, in which case it must be a path552            to a CA bundle to use. Defaults to ``True``. When set to553            ``False``, requests will accept any TLS certificate presented by554            the server, and will ignore hostname mismatches and/or expired555            certificates, which will make your application vulnerable to556            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.Response561        """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, cert581        )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 resp592 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.Response599        """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.Response610        """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.Response621        """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-like631            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.Response635        """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-like644            object to send in the body of the :class:`Request`.645        :param \*\*kwargs: Optional arguments that ``request`` takes.646        :rtype: requests.Response647        """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-like656            object to send in the body of the :class:`Request`.657        :param \*\*kwargs: Optional arguments that ``request`` takes.658        :rtype: requests.Response659        """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.Response669        """670 671        return self.request("DELETE", url, **kwargs)672 673    def send(self, request, **kwargs):674        """Send a given PreparedRequest.675 676        :rtype: requests.Response677        """678        # Set defaults that the hooks can utilize to ensure they always have679        # 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 hooks692        allow_redirects = kwargs.pop("allow_redirects", True)693        stream = kwargs.get("stream")694        hooks = request.hooks695 696        # Get the appropriate adapter to use697        adapter = self.get_adapter(url=request.url)698 699        # Start time (approximately) of the request700        start = preferred_clock()701 702        # Send the request703        r = adapter.send(request, **kwargs)704 705        # Total elapsed time of the request (approximately)706        elapsed = preferred_clock() - start707        r.elapsed = timedelta(seconds=elapsed)708 709        # Response manipulation hooks710        r = dispatch_hook("response", hooks, r, **kwargs)711 712        # Persist cookies713        if r.history:714 715            # If the hooks create history then we want those cookies too716            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 start732            history.insert(0, r)733            # Get the last request made734            r = history.pop()735            r.history = history736 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                pass745 746        if not stream:747            r.content748 749        return r750 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: dict756        """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 None761            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 configuration766            # 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 verify772                )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.BaseAdapter787        """788        for (prefix, adapter) in self.adapters.items():789 790            if url.lower().startswith(prefix.lower()):791                return adapter792 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] = adapter807        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 state815 816    def __setstate__(self, state):817        for attr, value in state.items():818            setattr(self, attr, value)819 820 821def session():822    """823    Returns a :class:`Session` for context-management.824 825    .. deprecated:: 1.0.0826 827        This method has been deprecated since version 1.0.0 and is only kept for828        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: Session832    """833    return Session()834 
codekingpro/portable-devtools · Team Ai