Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
sessions.py921 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"""8 9from __future__ import annotations10 11import os12import sys13import time14from collections import OrderedDict15from collections.abc import Generator, Mapping, MutableMapping16from datetime import timedelta17from typing import TYPE_CHECKING, Any, cast18 19from ._internal_utils import to_native_string20from ._types import is_prepared as _is_prepared21from .adapters import HTTPAdapter22from .auth import _basic_auth_str  # type: ignore[reportPrivateUsage]23from .compat import cookielib, urljoin, urlparse24from .cookies import (25    RequestsCookieJar,26    cookiejar_from_dict,27    extract_cookies_to_jar,28    merge_cookies,29)30from .exceptions import (31    ChunkedEncodingError,32    ContentDecodingError,33    InvalidSchema,34    TooManyRedirects,35)36from .hooks import default_hooks, dispatch_hook37 38# formerly defined here, reexposed here for backward compatibility39from .models import (  # noqa: F40140    DEFAULT_REDIRECT_LIMIT,41    REDIRECT_STATI,  # type: ignore[reportUnusedImport]42    PreparedRequest,43    Request,44    Response,45)46from .status_codes import codes47from .structures import CaseInsensitiveDict48from .utils import (  # noqa: F40149    DEFAULT_PORTS,50    default_headers,51    get_auth_from_url,52    get_environ_proxies,53    get_netrc_auth,54    requote_uri,55    resolve_proxies,56    rewind_body,57    should_bypass_proxies,  # type: ignore[reportUnusedImport]  # re-export for external consumers58    to_key_val_list,59)60 61if TYPE_CHECKING:62    from http.cookiejar import CookieJar63 64    from typing_extensions import Self, Unpack65 66    from . import _types as _t67    from .adapters import BaseAdapter68 69# Preferred clock, based on which one is more accurate on a given system.70if sys.platform == "win32":71    preferred_clock = time.perf_counter72else:73    preferred_clock = time.time74 75 76def merge_setting(77    request_setting: Any, session_setting: Any, dict_class: type = OrderedDict78) -> Any:79    """Determines appropriate setting for a given request, taking into account80    the explicit setting on that request, and the setting in the session. If a81    setting is a dictionary, they will be merged together using `dict_class`82    """83 84    if session_setting is None:85        return request_setting86 87    if request_setting is None:88        return session_setting89 90    # Bypass if not a dictionary (e.g. verify)91    if not (92        isinstance(session_setting, Mapping) and isinstance(request_setting, Mapping)93    ):94        return request_setting95 96    merged_setting = dict_class(to_key_val_list(session_setting))  # type: ignore[arg-type]  # isinstance narrows Any to Mapping[Unknown]97    merged_setting.update(to_key_val_list(request_setting))  # type: ignore[arg-type]98 99    # Remove keys that are set to None. Extract keys first to avoid altering100    # the dictionary during iteration.101    none_keys = [k for (k, v) in merged_setting.items() if v is None]102    for key in none_keys:103        del merged_setting[key]104 105    return merged_setting106 107 108def merge_hooks(109    request_hooks: _t.HooksType,110    session_hooks: _t.HooksType,111    dict_class: type = OrderedDict,112) -> _t.HooksType:113    """Properly merges both requests and session hooks.114 115    This is necessary because when request_hooks == {'response': []}, the116    merge breaks Session hooks entirely.117    """118    if session_hooks is None or session_hooks.get("response") == []:119        return request_hooks120 121    if request_hooks is None or request_hooks.get("response") == []:122        return session_hooks123 124    return merge_setting(request_hooks, session_hooks, dict_class)125 126 127class SessionRedirectMixin:128    max_redirects: int129    trust_env: bool130    cookies: RequestsCookieJar131 132    def send(self, request: PreparedRequest, **kwargs: Any) -> Response: ...133 134    def get_redirect_target(self, resp: Response) -> str | None:135        """Receives a Response. Returns a redirect URI or ``None``"""136        # Due to the nature of how requests processes redirects this method will137        # be called at least once upon the original response and at least twice138        # on each subsequent redirect response (if any).139        # If a custom mixin is used to handle this logic, it may be advantageous140        # to cache the redirect location onto the response object as a private141        # attribute.142        if resp.is_redirect:143            location = resp.headers["location"]144            # Currently the underlying http module on py3 decode headers145            # in latin1, but empirical evidence suggests that latin1 is very146            # rarely used with non-ASCII characters in HTTP headers.147            # It is more likely to get UTF8 header rather than latin1.148            # This causes incorrect handling of UTF8 encoded location headers.149            # To solve this, we re-encode the location in latin1.150            location = location.encode("latin1")151            return to_native_string(location, "utf8")152        return None153 154    def should_strip_auth(self, old_url: str, new_url: str) -> bool:155        """Decide whether Authorization header should be removed when redirecting"""156        old_parsed = urlparse(old_url)157        new_parsed = urlparse(new_url)158        if old_parsed.hostname != new_parsed.hostname:159            return True160        # Special case: allow http -> https redirect when using the standard161        # ports. This isn't specified by RFC 7235, but is kept to avoid162        # breaking backwards compatibility with older versions of requests163        # that allowed any redirects on the same host.164        if (165            old_parsed.scheme == "http"166            and old_parsed.port in (80, None)167            and new_parsed.scheme == "https"168            and new_parsed.port in (443, None)169        ):170            return False171 172        # Handle default port usage corresponding to scheme.173        changed_port = old_parsed.port != new_parsed.port174        changed_scheme = old_parsed.scheme != new_parsed.scheme175        default_port = (DEFAULT_PORTS.get(old_parsed.scheme, None), None)176        if (177            not changed_scheme178            and old_parsed.port in default_port179            and new_parsed.port in default_port180        ):181            return False182 183        # Standard case: root URI must match184        return changed_port or changed_scheme185 186    def resolve_redirects(187        self,188        resp: Response,189        req: PreparedRequest,190        stream: bool = False,191        timeout: _t.TimeoutType = None,192        verify: _t.VerifyType = True,193        cert: _t.CertType = None,194        proxies: dict[str, str] | None = None,195        yield_requests: bool = False,196        **adapter_kwargs: Any,197    ) -> Generator[Response, None, None]:198        """Receives a Response. Returns a generator of Responses or Requests."""199 200        hist: list[Response] = []  # keep track of history201 202        url = self.get_redirect_target(resp)203        previous_fragment = urlparse(req.url).fragment204        while url:205            prepared_request = req.copy()206 207            # Update history and keep track of redirects.208            resp.history = hist[:]209            hist.append(resp)210 211            try:212                resp.content  # Consume socket so it can be released213            except (ChunkedEncodingError, ContentDecodingError, RuntimeError):214                resp.raw.read(decode_content=False)215 216            if len(resp.history) >= self.max_redirects:217                raise TooManyRedirects(218                    f"Exceeded {self.max_redirects} redirects.", response=resp219                )220 221            # Release the connection back into the pool.222            resp.close()223 224            # Handle redirection without scheme (see: RFC 1808 Section 4)225            if url.startswith("//"):226                parsed_rurl = urlparse(resp.url)227                url = ":".join([to_native_string(parsed_rurl.scheme), url])228 229            # Normalize url case and attach previous fragment if needed (RFC 7231 7.1.2)230            parsed = urlparse(url)231            if parsed.fragment == "" and previous_fragment:232                parsed = parsed._replace(fragment=previous_fragment)233            elif parsed.fragment:234                previous_fragment = parsed.fragment235            url = parsed.geturl()236 237            # Facilitate relative 'location' headers, as allowed by RFC 7231.238            # (e.g. '/path/to/resource' instead of 'http://domain.tld/path/to/resource')239            # Compliant with RFC3986, we percent encode the url.240            if not parsed.netloc:241                url = urljoin(resp.url, requote_uri(url))242            else:243                url = requote_uri(url)244 245            prepared_request.url = to_native_string(url)246 247            self.rebuild_method(prepared_request, resp)248 249            # https://github.com/psf/requests/issues/1084250            if resp.status_code not in (251                codes.temporary_redirect,252                codes.permanent_redirect,253            ):254                # https://github.com/psf/requests/issues/3490255                purged_headers = ("Content-Length", "Content-Type", "Transfer-Encoding")256                for header in purged_headers:257                    prepared_request.headers.pop(header, None)258                prepared_request.body = None259 260            headers = prepared_request.headers261            headers.pop("Cookie", None)262 263            # Extract any cookies sent on the response to the cookiejar264            # in the new request. Because we've mutated our copied prepared265            # request, use the old one that we haven't yet touched.266            cookie_jar = cast("CookieJar", prepared_request._cookies)  # type: ignore[reportPrivateUsage]267            extract_cookies_to_jar(cookie_jar, req, resp.raw)268            merge_cookies(cookie_jar, self.cookies)269            prepared_request.prepare_cookies(cookie_jar)270 271            # Rebuild auth and proxy information.272            proxies = self.rebuild_proxies(prepared_request, proxies)273            self.rebuild_auth(prepared_request, resp)274 275            # A failed tell() sets `_body_position` to `object()`. This non-None276            # value ensures `rewindable` will be True, allowing us to raise an277            # UnrewindableBodyError, instead of hanging the connection.278            rewindable = prepared_request._body_position is not None and (  # type: ignore[reportPrivateUsage]279                "Content-Length" in headers or "Transfer-Encoding" in headers280            )281 282            # Attempt to rewind consumed file-like object.283            if rewindable:284                rewind_body(prepared_request)285 286            # Override the original request.287            req = prepared_request288 289            if yield_requests:290                yield req  # type: ignore[misc]  # Internal use only, returns PreparedRequest291            else:292                resp = self.send(293                    req,294                    stream=stream,295                    timeout=timeout,296                    verify=verify,297                    cert=cert,298                    proxies=proxies,299                    allow_redirects=False,300                    **adapter_kwargs,301                )302 303                extract_cookies_to_jar(self.cookies, prepared_request, resp.raw)304 305                # extract redirect url, if any, for the next loop306                url = self.get_redirect_target(resp)307                yield resp308 309    def rebuild_auth(310        self, prepared_request: PreparedRequest, response: Response311    ) -> None:312        """When being redirected we may want to strip authentication from the313        request to avoid leaking credentials. This method intelligently removes314        and reapplies authentication where possible to avoid credential loss.315        """316        original_request = response.request317        assert _is_prepared(original_request)318        assert _is_prepared(prepared_request)319 320        headers = prepared_request.headers321        original_url = original_request.url322        url = prepared_request.url323 324        if "Authorization" in headers and self.should_strip_auth(original_url, url):325            # If we get redirected to a new host, we should strip out any326            # authentication headers.327            del headers["Authorization"]328 329        # .netrc might have more auth for us on our new host.330        new_auth = get_netrc_auth(url) if self.trust_env else None331        if new_auth is not None:332            prepared_request.prepare_auth(new_auth)333 334    def rebuild_proxies(335        self,336        prepared_request: PreparedRequest,337        proxies: dict[str, str] | None,338    ) -> dict[str, str]:339        """This method re-evaluates the proxy configuration by considering the340        environment variables. If we are redirected to a URL covered by341        NO_PROXY, we strip the proxy configuration. Otherwise, we set missing342        proxy keys for this URL (in case they were stripped by a previous343        redirect).344 345        This method also replaces the Proxy-Authorization header where346        necessary.347 348        :rtype: dict349        """350        assert _is_prepared(prepared_request)351        headers = prepared_request.headers352        scheme = urlparse(prepared_request.url).scheme353        new_proxies = resolve_proxies(prepared_request, proxies, self.trust_env)354 355        if "Proxy-Authorization" in headers:356            del headers["Proxy-Authorization"]357 358        try:359            username, password = get_auth_from_url(new_proxies[scheme])360        except KeyError:361            username, password = None, None362 363        # urllib3 handles proxy authorization for us in the standard adapter.364        # Avoid appending this to TLS tunneled requests where it may be leaked.365        if not scheme.startswith("https") and username and password:366            headers["Proxy-Authorization"] = _basic_auth_str(username, password)367 368        return new_proxies369 370    def rebuild_method(371        self, prepared_request: PreparedRequest, response: Response372    ) -> None:373        """When being redirected we may want to change the method of the request374        based on certain specs or browser behavior.375        """376        method = prepared_request.method377 378        # https://tools.ietf.org/html/rfc7231#section-6.4.4379        if response.status_code == codes.see_other and method != "HEAD":380            method = "GET"381 382        # Do what the browsers do, despite standards...383        # First, turn 302s into GETs.384        if response.status_code == codes.found and method != "HEAD":385            method = "GET"386 387        # Second, if a POST is responded to with a 301, turn it into a GET.388        # This bizarre behaviour is explained in Issue 1704.389        if response.status_code == codes.moved and method == "POST":390            method = "GET"391 392        prepared_request.method = method393 394 395class Session(SessionRedirectMixin):396    """A Requests session.397 398    Provides cookie persistence, connection-pooling, and configuration.399 400    Basic Usage::401 402      >>> import requests403      >>> s = requests.Session()404      >>> s.get('https://httpbin.org/get')405      <Response [200]>406 407    Or as a context manager::408 409      >>> with requests.Session() as s:410      ...     s.get('https://httpbin.org/get')411      <Response [200]>412    """413 414    headers: CaseInsensitiveDict[str]415    auth: _t.AuthType416    proxies: dict[str, str]417    hooks: dict[str, list[_t.HookType]]418    params: MutableMapping[str, Any]419    stream: bool420    verify: _t.VerifyType421    cert: _t.CertType422    max_redirects: int423    trust_env: bool424    cookies: RequestsCookieJar425    adapters: MutableMapping[str, BaseAdapter]426 427    __attrs__: list[str] = [428        "headers",429        "cookies",430        "auth",431        "proxies",432        "hooks",433        "params",434        "verify",435        "cert",436        "adapters",437        "stream",438        "trust_env",439        "max_redirects",440    ]441 442    def __init__(self) -> None:443        #: A case-insensitive dictionary of headers to be sent on each444        #: :class:`Request <Request>` sent from this445        #: :class:`Session <Session>`.446        self.headers = default_headers()447 448        #: Default Authentication tuple or object to attach to449        #: :class:`Request <Request>`.450        self.auth = None451 452        #: Dictionary mapping protocol or protocol and host to the URL of the proxy453        #: (e.g. {'http': 'foo.bar:3128', 'http://host.name': 'foo.bar:4012'}) to454        #: be used on each :class:`Request <Request>`.455        self.proxies = {}456 457        #: Event-handling hooks.458        self.hooks = default_hooks()459 460        #: Dictionary of querystring data to attach to each461        #: :class:`Request <Request>`. The dictionary values may be lists for462        #: representing multivalued query parameters.463        self.params = {}464 465        #: Stream response content default.466        self.stream = False467 468        #: SSL Verification default.469        #: Defaults to `True`, requiring requests to verify the TLS certificate at the470        #: remote end.471        #: If verify is set to `False`, requests will accept any TLS certificate472        #: presented by the server, and will ignore hostname mismatches and/or473        #: expired certificates, which will make your application vulnerable to474        #: man-in-the-middle (MitM) attacks.475        #: Only set this to `False` for testing.476        #: If verify is set to a string, it must be the path to a CA bundle file477        #: that will be used to verify the TLS certificate.478        self.verify = True479 480        #: SSL client certificate default, if String, path to ssl client481        #: cert file (.pem). If Tuple, ('cert', 'key') pair.482        self.cert = None483 484        #: Maximum number of redirects allowed. If the request exceeds this485        #: limit, a :class:`TooManyRedirects` exception is raised.486        #: This defaults to requests.models.DEFAULT_REDIRECT_LIMIT, which is487        #: 30.488        self.max_redirects = DEFAULT_REDIRECT_LIMIT489 490        #: Trust environment settings for proxy configuration, default491        #: authentication and similar.492        self.trust_env = True493 494        #: A CookieJar containing all currently outstanding cookies set on this495        #: session. By default it is a496        #: :class:`RequestsCookieJar <requests.cookies.RequestsCookieJar>`, but497        #: may be any other ``cookielib.CookieJar`` compatible object.498        self.cookies = cookiejar_from_dict({})499 500        # Default connection adapters.501        self.adapters = OrderedDict()502        self.mount("https://", HTTPAdapter())503        self.mount("http://", HTTPAdapter())504 505    def __enter__(self) -> Self:506        return self507 508    def __exit__(self, *args: Any) -> None:509        self.close()510 511    def prepare_request(self, request: Request) -> PreparedRequest:512        """Constructs a :class:`PreparedRequest <PreparedRequest>` for513        transmission and returns it. The :class:`PreparedRequest` has settings514        merged from the :class:`Request <Request>` instance and those of the515        :class:`Session`.516 517        :param request: :class:`Request` instance to prepare with this518            session's settings.519        :rtype: requests.PreparedRequest520        """521        url = cast("_t.UriType", request.url)522        method = cast(str, request.method)523 524        cookies = request.cookies or {}525 526        # Bootstrap CookieJar.527        if not isinstance(cookies, cookielib.CookieJar):528            cookies = cookiejar_from_dict(cookies)529 530        # Merge with session cookies531        merged_cookies = merge_cookies(532            merge_cookies(RequestsCookieJar(), self.cookies), cookies533        )534 535        # Set environment's basic authentication if not explicitly set.536        auth = request.auth537        if self.trust_env and not auth and not self.auth:538            auth = get_netrc_auth(url)539 540        p = PreparedRequest()541        p.prepare(542            method=method.upper(),543            url=url,544            files=request.files,545            data=request.data,546            json=request.json,547            headers=merge_setting(548                request.headers, self.headers, dict_class=CaseInsensitiveDict549            ),550            params=merge_setting(request.params, self.params),551            auth=merge_setting(auth, self.auth),552            cookies=merged_cookies,553            hooks=merge_hooks(request.hooks, self.hooks),554        )555        return p556 557    def request(558        self,559        method: str,560        url: _t.UriType,561        params: _t.ParamsType = None,562        data: _t.DataType = None,563        headers: Mapping[str, str | bytes] | None = None,564        cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None,565        files: _t.FilesType = None,566        auth: _t.AuthType = None,567        timeout: _t.TimeoutType = None,568        allow_redirects: bool = True,569        proxies: dict[str, str] | None = None,570        hooks: _t.HooksInputType | None = None,571        stream: bool | None = None,572        verify: _t.VerifyType | None = None,573        cert: _t.CertType = None,574        json: _t.JsonType = None,575    ) -> Response:576        """Constructs a :class:`Request <Request>`, prepares it and sends it.577        Returns :class:`Response <Response>` object.578 579        :param method: method for the new :class:`Request` object.580        :param url: URL for the new :class:`Request` object.581        :param params: (optional) Dictionary or bytes to be sent in the query582            string for the :class:`Request`.583        :param data: (optional) Dictionary, list of tuples, bytes, or file-like584            object to send in the body of the :class:`Request`.585        :param json: (optional) json to send in the body of the586            :class:`Request`.587        :param headers: (optional) Dictionary of HTTP Headers to send with the588            :class:`Request`.589        :param cookies: (optional) Dict or CookieJar object to send with the590            :class:`Request`.591        :param files: (optional) Dictionary of ``'filename': file-like-objects``592            for multipart encoding upload.593        :param auth: (optional) Auth tuple or callable to enable594            Basic/Digest/Custom HTTP Auth.595        :param timeout: (optional) How many seconds to wait for the server to send596            data before giving up, as a float, or a :ref:`(connect timeout,597            read timeout) <timeouts>` tuple.598        :type timeout: float or tuple599        :param allow_redirects: (optional) Set to True by default.600        :type allow_redirects: bool601        :param proxies: (optional) Dictionary mapping protocol or protocol and602            hostname to the URL of the proxy.603        :param hooks: (optional) Dictionary mapping hook name to one event or604            list of events, event must be callable.605        :param stream: (optional) whether to immediately download the response606            content. Defaults to ``False``.607        :param verify: (optional) Either a boolean, in which case it controls whether we verify608            the server's TLS certificate, or a string, in which case it must be a path609            to a CA bundle to use. Defaults to ``True``. When set to610            ``False``, requests will accept any TLS certificate presented by611            the server, and will ignore hostname mismatches and/or expired612            certificates, which will make your application vulnerable to613            man-in-the-middle (MitM) attacks. Setting verify to ``False``614            may be useful during local development or testing.615        :param cert: (optional) if String, path to ssl client cert file (.pem).616            If Tuple, ('cert', 'key') pair.617        :rtype: requests.Response618        """619        if isinstance(url, bytes):620            url = url.decode("utf-8")621 622        # Create the Request.623        req = Request(624            method=method.upper(),625            url=url,626            headers=headers,627            files=files,628            data=data or {},629            json=json,630            params=params or {},631            auth=auth,632            cookies=cookies,633            hooks=hooks,634        )635        prep = self.prepare_request(req)636 637        assert _is_prepared(prep)638 639        proxies = proxies or {}640 641        settings = self.merge_environment_settings(642            prep.url, proxies, stream, verify, cert643        )644 645        # Send the request.646        send_kwargs = {647            "timeout": timeout,648            "allow_redirects": allow_redirects,649        }650        send_kwargs.update(settings)651        resp = self.send(prep, **send_kwargs)652 653        return resp654 655    def get(656        self,657        url: _t.UriType,658        params: _t.ParamsType = None,659        **kwargs: Unpack[_t.GetKwargs],660    ) -> Response:661        r"""Sends a GET request. Returns :class:`Response` object.662 663        :param url: URL for the new :class:`Request` object.664        :param params: (optional) Dictionary, list of tuples or bytes to send665        in the query string for the :class:`Request`.666        :param \*\*kwargs: Optional arguments that ``request`` takes.667        :rtype: requests.Response668        """669 670        kwargs.setdefault("allow_redirects", True)671        return self.request("GET", url, params=params, **kwargs)672 673    def options(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:674        r"""Sends a OPTIONS request. Returns :class:`Response` object.675 676        :param url: URL for the new :class:`Request` object.677        :param \*\*kwargs: Optional arguments that ``request`` takes.678        :rtype: requests.Response679        """680 681        kwargs.setdefault("allow_redirects", True)682        return self.request("OPTIONS", url, **kwargs)683 684    def head(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:685        r"""Sends a HEAD request. Returns :class:`Response` object.686 687        :param url: URL for the new :class:`Request` object.688        :param \*\*kwargs: Optional arguments that ``request`` takes.689        :rtype: requests.Response690        """691 692        kwargs.setdefault("allow_redirects", False)693        return self.request("HEAD", url, **kwargs)694 695    def post(696        self,697        url: _t.UriType,698        data: _t.DataType = None,699        json: _t.JsonType = None,700        **kwargs: Unpack[_t.PostKwargs],701    ) -> Response:702        r"""Sends a POST request. Returns :class:`Response` object.703 704        :param url: URL for the new :class:`Request` object.705        :param data: (optional) Dictionary, list of tuples, bytes, or file-like706            object to send in the body of the :class:`Request`.707        :param json: (optional) json to send in the body of the :class:`Request`.708        :param \*\*kwargs: Optional arguments that ``request`` takes.709        :rtype: requests.Response710        """711 712        return self.request("POST", url, data=data, json=json, **kwargs)713 714    def put(715        self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs]716    ) -> Response:717        r"""Sends a PUT request. Returns :class:`Response` object.718 719        :param url: URL for the new :class:`Request` object.720        :param data: (optional) Dictionary, list of tuples, bytes, or file-like721            object to send in the body of the :class:`Request`.722        :param \*\*kwargs: Optional arguments that ``request`` takes.723        :rtype: requests.Response724        """725 726        return self.request("PUT", url, data=data, **kwargs)727 728    def patch(729        self, url: _t.UriType, data: _t.DataType = None, **kwargs: Unpack[_t.DataKwargs]730    ) -> Response:731        r"""Sends a PATCH request. Returns :class:`Response` object.732 733        :param url: URL for the new :class:`Request` object.734        :param data: (optional) Dictionary, list of tuples, bytes, or file-like735            object to send in the body of the :class:`Request`.736        :param \*\*kwargs: Optional arguments that ``request`` takes.737        :rtype: requests.Response738        """739 740        return self.request("PATCH", url, data=data, **kwargs)741 742    def delete(self, url: _t.UriType, **kwargs: Unpack[_t.RequestKwargs]) -> Response:743        r"""Sends a DELETE request. Returns :class:`Response` object.744 745        :param url: URL for the new :class:`Request` object.746        :param \*\*kwargs: Optional arguments that ``request`` takes.747        :rtype: requests.Response748        """749 750        return self.request("DELETE", url, **kwargs)751 752    def send(self, request: PreparedRequest, **kwargs: Any) -> Response:753        """Send a given PreparedRequest.754 755        :rtype: requests.Response756        """757        # Set defaults that the hooks can utilize to ensure they always have758        # the correct parameters to reproduce the previous request.759        kwargs.setdefault("stream", self.stream)760        kwargs.setdefault("verify", self.verify)761        kwargs.setdefault("cert", self.cert)762        if "proxies" not in kwargs:763            kwargs["proxies"] = resolve_proxies(request, self.proxies, self.trust_env)764 765        # It's possible that users might accidentally send a Request object.766        # Guard against that specific failure case.767        if isinstance(request, Request):768            raise ValueError("You can only send PreparedRequests.")769 770        assert _is_prepared(request)771 772        # Set up variables needed for resolve_redirects and dispatching of hooks773        allow_redirects = kwargs.pop("allow_redirects", True)774        stream = kwargs.get("stream")775        hooks = request.hooks776 777        # Get the appropriate adapter to use778        adapter = self.get_adapter(url=request.url)779 780        # Start time (approximately) of the request781        start = preferred_clock()782 783        # Send the request784        r = adapter.send(request, **kwargs)785 786        # Total elapsed time of the request (approximately)787        elapsed = preferred_clock() - start788        r.elapsed = timedelta(seconds=elapsed)789 790        # Response manipulation hooks791        r = dispatch_hook("response", hooks, r, **kwargs)792 793        # Persist cookies794        if r.history:795            # If the hooks create history then we want those cookies too796            for resp in r.history:797                extract_cookies_to_jar(self.cookies, resp.request, resp.raw)798 799        extract_cookies_to_jar(self.cookies, request, r.raw)800 801        # Resolve redirects if allowed.802        if allow_redirects:803            # Redirect resolving generator.804            gen = self.resolve_redirects(r, request, **kwargs)805            history = [resp for resp in gen]806        else:807            history = []808 809        # Shuffle things around if there's history.810        if history:811            # Insert the first (original) request at the start812            history.insert(0, r)813            # Get the last request made814            r = history.pop()815            r.history = history816 817        # If redirects aren't being followed, store the response on the Request for Response.next().818        if not allow_redirects:819            try:820                r._next = next(  # type: ignore[assignment]  # yield_requests=True returns PreparedRequest821                    self.resolve_redirects(r, request, yield_requests=True, **kwargs)822                )823            except StopIteration:824                pass825 826        if not stream:827            r.content828 829        return r830 831    def merge_environment_settings(832        self,833        url: str,834        proxies: dict[str, str] | None,835        stream: bool | None,836        verify: _t.VerifyType | None,837        cert: _t.CertType,838    ) -> dict[str, Any]:839        """840        Check the environment and merge it with some settings.841 842        :rtype: dict843        """844        # Gather clues from the surrounding environment.845        if self.trust_env:846            # Set environment's proxies.847            no_proxy = proxies.get("no_proxy") if proxies is not None else None848            env_proxies = get_environ_proxies(url, no_proxy=no_proxy)849            if proxies is not None:850                for k, v in env_proxies.items():851                    proxies.setdefault(k, v)852 853            # Look for requests environment configuration854            # and be compatible with cURL.855            if verify is True or verify is None:856                verify = (857                    os.environ.get("REQUESTS_CA_BUNDLE")858                    or os.environ.get("CURL_CA_BUNDLE")859                    or verify860                )861 862        # Merge all the kwargs.863        proxies = merge_setting(proxies, self.proxies)864        stream = merge_setting(stream, self.stream)865        verify = merge_setting(verify, self.verify)866        cert = merge_setting(cert, self.cert)867 868        return {"proxies": proxies, "stream": stream, "verify": verify, "cert": cert}869 870    def get_adapter(self, url: str) -> BaseAdapter:871        """872        Returns the appropriate connection adapter for the given URL.873 874        :rtype: requests.adapters.BaseAdapter875        """876        for prefix, adapter in self.adapters.items():877            if url.lower().startswith(prefix.lower()):878                return adapter879 880        # Nothing matches :-/881        raise InvalidSchema(f"No connection adapters were found for {url!r}")882 883    def close(self) -> None:884        """Closes all adapters and as such the session"""885        for v in self.adapters.values():886            v.close()887 888    def mount(self, prefix: str, adapter: BaseAdapter) -> None:889        """Registers a connection adapter to a prefix.890 891        Adapters are sorted in descending order by prefix length.892        """893        self.adapters[prefix] = adapter894        keys_to_move = [k for k in self.adapters if len(k) < len(prefix)]895 896        for key in keys_to_move:897            self.adapters[key] = self.adapters.pop(key)898 899    def __getstate__(self) -> dict[str, Any]:900        state = {attr: getattr(self, attr, None) for attr in self.__attrs__}901        return state902 903    def __setstate__(self, state: dict[str, Any]) -> None:904        for attr, value in state.items():905            setattr(self, attr, value)906 907 908def session() -> Session:909    """910    Returns a :class:`Session` for context-management.911 912    .. deprecated:: 1.0.0913 914        This method has been deprecated since version 1.0.0 and is only kept for915        backwards compatibility. New code should use :class:`~requests.sessions.Session`916        to create a session. This may be removed at a future date.917 918    :rtype: Session919    """920    return Session()921 
codekingpro/portable-devtools · Team Ai