Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
models.py1181 linesDownload Raw Back to requests
1"""2requests.models3~~~~~~~~~~~~~~~4 5This module contains the primary objects that power Requests.6"""7 8from __future__ import annotations9 10import datetime11 12# Import encoding now, to avoid implicit import later.13# Implicit import within threads may cause LookupError when standard library is in a ZIP,14# such as in Embedded Python. See https://github.com/psf/requests/issues/3578.15import encodings.idna  # noqa: F401  # type: ignore[reportUnusedImport]16from collections.abc import Callable, Generator, Iterable, Iterator, Mapping17from io import UnsupportedOperation18from typing import (19    TYPE_CHECKING,20    Any,21    Final,22    Literal,23    cast,24    overload,25)26 27from urllib3.exceptions import (28    DecodeError,29    LocationParseError,30    ProtocolError,31    ReadTimeoutError,32    SSLError,33)34from urllib3.fields import RequestField35from urllib3.filepost import encode_multipart_formdata36from urllib3.util import parse_url37 38from ._internal_utils import to_native_string, unicode_is_ascii39from ._types import SupportsRead as _SupportsRead40from .auth import HTTPBasicAuth41from .compat import (42    JSONDecodeError,43    basestring,44    builtin_str,45    chardet,46    cookielib,47    urlencode,48    urlsplit,49    urlunparse,50)51from .compat import json as complexjson52from .cookies import (53    _copy_cookie_jar,  # type: ignore[reportPrivateUsage]54    cookiejar_from_dict,55    get_cookie_header,56)57from .exceptions import (58    ChunkedEncodingError,59    ConnectionError,60    ContentDecodingError,61    HTTPError,62    InvalidJSONError,63    InvalidURL,64    MissingSchema,65    StreamConsumedError,66)67from .exceptions import JSONDecodeError as RequestsJSONDecodeError68from .exceptions import SSLError as RequestsSSLError69from .hooks import default_hooks70from .status_codes import codes71from .structures import CaseInsensitiveDict72from .utils import (73    check_header_validity,74    get_auth_from_url,75    guess_filename,76    guess_json_utf,77    iter_slices,78    parse_header_links,79    requote_uri,80    stream_decode_response_unicode,81    super_len,82    to_key_val_list,83)84 85if TYPE_CHECKING:86    from http.cookiejar import CookieJar87 88    from typing_extensions import Self89 90    from . import _types as _t91    from .adapters import HTTPAdapter92    from .cookies import RequestsCookieJar93 94#: The set of HTTP status codes that indicate an automatically95#: processable redirect.96REDIRECT_STATI: Final[tuple[int, ...]] = (  # type: ignore[assignment]97    codes.moved,  # 30198    codes.found,  # 30299    codes.other,  # 303100    codes.temporary_redirect,  # 307101    codes.permanent_redirect,  # 308102)103 104DEFAULT_REDIRECT_LIMIT: int = 30105CONTENT_CHUNK_SIZE: int = 10 * 1024106ITER_CHUNK_SIZE: int = 512107 108 109class RequestEncodingMixin:110    url: str | None111 112    @property113    def path_url(self) -> str:114        """Build the path URL to use."""115 116        url: list[str] = []117 118        p = urlsplit(cast(str, self.url))119 120        path = p.path121        if not path:122            path = "/"123 124        url.append(path)125 126        query = p.query127        if query:128            url.append("?")129            url.append(query)130 131        return "".join(url)132 133    @overload134    @staticmethod135    def _encode_params(data: str) -> str: ...136 137    @overload138    @staticmethod139    def _encode_params(data: bytes) -> bytes: ...140 141    @overload142    @staticmethod143    def _encode_params(144        data: _t.SupportsRead[str | bytes],145    ) -> _t.SupportsRead[str | bytes]: ...146 147    @overload148    @staticmethod149    def _encode_params(data: _t.KVDataType) -> str: ...150 151    @staticmethod152    def _encode_params(153        data: _t.EncodableDataType,154    ) -> str | bytes | _t.SupportsRead[str | bytes]:155        """Encode parameters in a piece of data.156 157        Will successfully encode parameters when passed as a dict or a list of158        2-tuples. Order is retained if data is a list of 2-tuples but arbitrary159        if parameters are supplied as a dict.160        """161 162        if isinstance(data, (str, bytes)):163            return data164        elif isinstance(data, _SupportsRead):165            return data166        elif hasattr(data, "__iter__"):167            result: list[tuple[bytes, bytes]] = []168            for k, vs in to_key_val_list(data):169                if isinstance(vs, basestring) or not hasattr(vs, "__iter__"):170                    vs = [vs]171                for v in vs:172                    if v is not None:173                        result.append(174                            (175                                k.encode("utf-8") if isinstance(k, str) else k,176                                v.encode("utf-8") if isinstance(v, str) else v,177                            )178                        )179            return urlencode(result, doseq=True)180        else:181            return data  # type: ignore[return-value]  # unreachable for valid _t.DataType182 183    @staticmethod184    def _encode_files(185        files: _t.FilesType, data: _t.RawDataType | None186    ) -> tuple[bytes, str]:187        """Build the body for a multipart/form-data request.188 189        Will successfully encode files when passed as a dict or a list of190        tuples. Order is retained if data is a list of tuples but arbitrary191        if parameters are supplied as a dict.192        The tuples may be 2-tuples (filename, fileobj), 3-tuples (filename, fileobj, contentype)193        or 4-tuples (filename, fileobj, contentype, custom_headers).194        """195        if not files:196            raise ValueError("Files must be provided.")197        elif isinstance(data, basestring):198            raise ValueError("Data must not be a string.")199 200        new_fields: list[RequestField | tuple[str, bytes]] = []201        fields = to_key_val_list(data or {})202        files = to_key_val_list(files or {})203 204        for field, val in fields:205            if isinstance(val, basestring) or not hasattr(val, "__iter__"):206                val = [val]207            for v in val:208                if v is not None:209                    # Don't call str() on bytestrings: in Py3 it all goes wrong.210                    if not isinstance(v, bytes):211                        v = str(v)212 213                    new_fields.append(214                        (215                            field.decode("utf-8")216                            if isinstance(field, bytes)217                            else field,218                            v.encode("utf-8") if isinstance(v, str) else v,219                        )220                    )221 222        for k, v in files:223            # support for explicit filename224            ft = None225            fh = None226            if isinstance(v, (tuple, list)):227                if len(v) == 2:228                    fn, fp = v229                elif len(v) == 3:230                    fn, fp, ft = v231                else:232                    fn, fp, ft, fh = v233            else:234                fn = guess_filename(v) or k235                fp = v236 237            if isinstance(fp, (str, bytes, bytearray)):238                fdata = fp239            elif isinstance(fp, _SupportsRead):  # type: ignore[reportUnnecessaryIsInstance]  # defensive check for untyped callers240                fdata = fp.read()241            elif fp is None:  # type: ignore[reportUnnecessaryComparison]  # defensive check for untyped callers242                continue243            else:244                fdata = fp245 246            rf = RequestField(name=k, data=fdata, filename=fn, headers=fh)247            rf.make_multipart(content_type=ft)248            new_fields.append(rf)249 250        body, content_type = encode_multipart_formdata(new_fields)251 252        return body, content_type253 254 255class RequestHooksMixin:256    hooks: dict[str, list[_t.HookType]]257 258    def register_hook(259        self, event: str, hook: Iterable[_t.HookType] | _t.HookType260    ) -> None:261        """Properly register a hook."""262 263        if event not in self.hooks:264            raise ValueError(f'Unsupported event specified, with event name "{event}"')265 266        if isinstance(hook, Callable):267            self.hooks[event].append(hook)268        elif hasattr(hook, "__iter__"):269            self.hooks[event].extend(h for h in hook if isinstance(h, Callable))  # type: ignore[reportUnnecessaryIsInstance]  # defensive runtime filter270 271    def deregister_hook(self, event: str, hook: _t.HookType) -> bool:272        """Deregister a previously registered hook.273        Returns True if the hook existed, False if not.274        """275 276        try:277            self.hooks[event].remove(hook)278            return True279        except ValueError:280            return False281 282 283class Request(RequestHooksMixin):284    """A user-created :class:`Request <Request>` object.285 286    Used to prepare a :class:`PreparedRequest <PreparedRequest>`, which is sent to the server.287 288    :param method: HTTP method to use.289    :param url: URL to send.290    :param headers: dictionary of headers to send.291    :param files: dictionary of {filename: fileobject} files to multipart upload.292    :param data: the body to attach to the request. If a dictionary or293        list of tuples ``[(key, value)]`` is provided, form-encoding will294        take place.295    :param json: json for the body to attach to the request (if files or data is not specified).296    :param params: URL parameters to append to the URL. If a dictionary or297        list of tuples ``[(key, value)]`` is provided, form-encoding will298        take place.299    :param auth: Auth handler or (user, pass) tuple.300    :param cookies: dictionary or CookieJar of cookies to attach to this request.301    :param hooks: dictionary of callback hooks, for internal usage.302 303    Usage::304 305      >>> import requests306      >>> req = requests.Request('GET', 'https://httpbin.org/get')307      >>> req.prepare()308      <PreparedRequest [GET]>309    """310 311    method: str | None312    url: _t.UriType | None313    headers: CaseInsensitiveDict[str] | Mapping[str, str | bytes] | None314    files: _t.FilesType315    data: _t.DataType316    json: _t.JsonType317    params: _t.ParamsType318    auth: _t.AuthType319    cookies: RequestsCookieJar | CookieJar | dict[str, str] | None320 321    def __init__(322        self,323        method: str | None = None,324        url: _t.UriType | None = None,325        headers: Mapping[str, str | bytes] | None = None,326        files: _t.FilesType = None,327        data: _t.DataType = None,328        params: _t.ParamsType = None,329        auth: _t.AuthType = None,330        cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None,331        hooks: _t.HooksInputType | None = None,332        json: _t.JsonType = None,333    ) -> None:334        # Default empty dicts for dict params.335        data = [] if data is None else data336        files = [] if files is None else files337        headers = {} if headers is None else headers338        params = {} if params is None else params339        hooks = {} if hooks is None else hooks340 341        self.hooks = default_hooks()342        for k, v in list(hooks.items()):343            self.register_hook(event=k, hook=v)344 345        self.method = method346        self.url = url347        self.headers = headers348        self.files = files349        self.data = data350        self.json = json351        self.params = params352        self.auth = auth353        self.cookies = cookies354 355    def __repr__(self) -> str:356        return f"<Request [{self.method}]>"357 358    def prepare(self) -> PreparedRequest:359        """Constructs a :class:`PreparedRequest <PreparedRequest>` for transmission and returns it."""360        p = PreparedRequest()361        p.prepare(362            method=self.method,363            url=self.url,364            headers=self.headers,365            files=self.files,366            data=self.data,367            json=self.json,368            params=self.params,369            auth=self.auth,370            cookies=self.cookies,371            hooks=self.hooks,372        )373        return p374 375 376class PreparedRequest(RequestEncodingMixin, RequestHooksMixin):377    """The fully mutable :class:`PreparedRequest <PreparedRequest>` object,378    containing the exact bytes that will be sent to the server.379 380    Instances are generated from a :class:`Request <Request>` object, and381    should not be instantiated manually; doing so may produce undesirable382    effects.383 384    Usage::385 386      >>> import requests387      >>> req = requests.Request('GET', 'https://httpbin.org/get')388      >>> r = req.prepare()389      >>> r390      <PreparedRequest [GET]>391 392      >>> s = requests.Session()393      >>> s.send(r)394      <Response [200]>395    """396 397    method: str | None398    url: str | None399    headers: CaseInsensitiveDict[str | bytes]400    _cookies: RequestsCookieJar | CookieJar | None401    body: _t.BodyType402    hooks: dict[str, list[_t.HookType]]403    _body_position: int | object | None404 405    def __init__(self) -> None:406        #: HTTP verb to send to the server.407        self.method = None408        #: HTTP URL to send the request to.409        self.url = None410        #: dictionary of HTTP headers.411        self.headers = None  # type: ignore[assignment]412        # The `CookieJar` used to create the Cookie header will be stored here413        # after prepare_cookies is called414        self._cookies = None415        #: request body to send to the server.416        self.body = None417        #: dictionary of callback hooks, for internal usage.418        self.hooks = default_hooks()419        #: integer denoting starting position of a readable file-like body.420        self._body_position = None421 422    def prepare(423        self,424        method: str | None = None,425        url: _t.UriType | None = None,426        headers: Mapping[str, str | bytes] | None = None,427        files: _t.FilesType = None,428        data: _t.DataType = None,429        params: _t.ParamsType = None,430        auth: _t.AuthType = None,431        cookies: RequestsCookieJar | CookieJar | dict[str, str] | None = None,432        hooks: _t.HooksInputType | None = None,433        json: _t.JsonType = None,434    ) -> None:435        """Prepares the entire request with the given parameters."""436 437        url = cast("_t.UriType", url)438        self.prepare_method(method)439        self.prepare_url(url, params)440        self.prepare_headers(headers)441        self.prepare_cookies(cookies)442        self.prepare_body(data, files, json)443        self.prepare_auth(auth, url)444 445        # Note that prepare_auth must be last to enable authentication schemes446        # such as OAuth to work on a fully prepared request.447 448        # This MUST go after prepare_auth. Authenticators could add a hook449        self.prepare_hooks(hooks)450 451    def __repr__(self) -> str:452        return f"<PreparedRequest [{self.method}]>"453 454    def copy(self) -> PreparedRequest:455        p = PreparedRequest()456        p.method = self.method457        p.url = self.url458        p.headers = self.headers.copy() if self.headers is not None else None  # type: ignore[assignment]459        p._cookies = _copy_cookie_jar(self._cookies)460        p.body = self.body461        p.hooks = self.hooks462        p._body_position = self._body_position463        return p464 465    def prepare_method(self, method: str | None) -> None:466        """Prepares the given HTTP method."""467        self.method = method468        if self.method is not None:469            self.method = to_native_string(self.method.upper())470 471    @staticmethod472    def _get_idna_encoded_host(host: str) -> str:473        import idna474 475        try:476            host = idna.encode(host, uts46=True).decode("utf-8")477        except idna.IDNAError:478            raise UnicodeError479        return host480 481    def prepare_url(482        self,483        url: _t.UriType,484        params: _t.ParamsType,485    ) -> None:486        """Prepares the given HTTP URL."""487        #: Accept objects that have string representations.488        #: We're unable to blindly call unicode/str functions489        #: as this will include the bytestring indicator (b'')490        #: on python 3.x.491        #: https://github.com/psf/requests/pull/2238492        if isinstance(url, bytes):493            url = url.decode("utf8")494        else:495            url = str(url)496 497        # Remove leading whitespaces from url498        url = url.lstrip()499 500        # Don't do any URL preparation for non-HTTP schemes like `mailto`,501        # `data` etc to work around exceptions from `url_parse`, which502        # handles RFC 3986 only.503        if ":" in url and not url.lower().startswith("http"):504            self.url = url505            return506 507        # Support for unicode domain names and paths.508        try:509            scheme, auth, host, port, path, query, fragment = parse_url(url)510        except LocationParseError as e:511            raise InvalidURL(*e.args)512 513        if not scheme:514            raise MissingSchema(515                f"Invalid URL {url!r}: No scheme supplied. "516                f"Perhaps you meant https://{url}?"517            )518 519        if not host:520            raise InvalidURL(f"Invalid URL {url!r}: No host supplied")521 522        # In general, we want to try IDNA encoding the hostname if the string contains523        # non-ASCII characters. This allows users to automatically get the correct IDNA524        # behaviour. For strings containing only ASCII characters, we need to also verify525        # it doesn't start with a wildcard (*), before allowing the unencoded hostname.526        if not unicode_is_ascii(host):527            try:528                host = self._get_idna_encoded_host(host)529            except UnicodeError:530                raise InvalidURL("URL has an invalid label.")531        elif host.startswith(("*", ".")):532            raise InvalidURL("URL has an invalid label.")533 534        # Carefully reconstruct the network location535        netloc = auth or ""536        if netloc:537            netloc += "@"538        netloc += host539        if port:540            netloc += f":{port}"541 542        # Bare domains aren't valid URLs.543        if not path:544            path = "/"545 546        if isinstance(params, (str, bytes)):547            params = to_native_string(params)548 549        if params is not None:550            enc_params = self._encode_params(params)551        else:552            enc_params = ""553 554        if enc_params:555            if query:556                query = f"{query}&{enc_params}"557            else:558                query = enc_params559 560        url = requote_uri(urlunparse((scheme, netloc, path, "", query, fragment)))561        self.url = url562 563    def prepare_headers(self, headers: Mapping[str, str | bytes] | None) -> None:564        """Prepares the given HTTP headers."""565 566        self.headers = CaseInsensitiveDict()567        if headers:568            for header in headers.items():569                # Raise exception on invalid header value.570                check_header_validity(header)571                name, value = header572                self.headers[to_native_string(name)] = value573 574    def prepare_body(575        self, data: _t.DataType, files: _t.FilesType, json: _t.JsonType = None576    ) -> None:577        """Prepares the given HTTP body data."""578 579        # Check if file, fo, generator, iterator.580        # If not, run through normal process.581 582        # Nottin' on you.583        body = None584        content_type = None585 586        if not data and json is not None:587            # urllib3 requires a bytes-like body. Python 2's json.dumps588            # provides this natively, but Python 3 gives a Unicode string.589            content_type = "application/json"590 591            try:592                body = complexjson.dumps(json, allow_nan=False)593            except ValueError as ve:594                raise InvalidJSONError(ve, request=self)595 596            if not isinstance(body, bytes):597                body = body.encode("utf-8")598 599        if isinstance(data, Iterable) and not isinstance(600            data, (str, bytes, list, tuple, Mapping)601        ):602            try:603                length = super_len(data)604            except (TypeError, AttributeError, UnsupportedOperation):605                length = None606 607            body = data608 609            if getattr(body, "tell", None) is not None:610                # Record the current file position before reading.611                # This will allow us to rewind a file in the event612                # of a redirect.613                try:614                    self._body_position = body.tell()  # type: ignore[union-attr]  # guarded by getattr check615                except OSError:616                    # This differentiates from None, allowing us to catch617                    # a failed `tell()` later when trying to rewind the body618                    self._body_position = object()619 620            if files:621                raise NotImplementedError(622                    "Streamed bodies and files are mutually exclusive."623                )624 625            if length:626                self.headers["Content-Length"] = builtin_str(length)627            else:628                self.headers["Transfer-Encoding"] = "chunked"629        else:630            # After is_stream filtering, remaining data is raw (not streamed)631            raw_data = cast("_t.RawDataType | None", data)632 633            # Multi-part file uploads.634            if files:635                (body, content_type) = self._encode_files(files, raw_data)636            else:637                if raw_data:638                    body = self._encode_params(raw_data)639                    if isinstance(data, basestring) or isinstance(data, _SupportsRead):640                        content_type = None641                    else:642                        content_type = "application/x-www-form-urlencoded"643 644            self.prepare_content_length(body)645 646            # Add content-type if it wasn't explicitly provided.647            if content_type and ("content-type" not in self.headers):648                self.headers["Content-Type"] = content_type649 650        self.body = body  # type: ignore[assignment]  # body transforms from DataType to BodyType651 652    def prepare_content_length(self, body: _t.BodyType) -> None:653        """Prepare Content-Length header based on request method and body"""654        if body is not None:655            length = super_len(body)656            if length:657                # If length exists, set it. Otherwise, we fallback658                # to Transfer-Encoding: chunked.659                self.headers["Content-Length"] = builtin_str(length)660        elif (661            self.method not in ("GET", "HEAD")662            and self.headers.get("Content-Length") is None663        ):664            # Set Content-Length to 0 for methods that can have a body665            # but don't provide one. (i.e. not GET or HEAD)666            self.headers["Content-Length"] = "0"667 668    def prepare_auth(669        self,670        auth: _t.AuthType,671        url: _t.UriType = "",672    ) -> None:673        """Prepares the given HTTP auth data."""674 675        # If no Auth is explicitly provided, extract it from the URL first.676        if auth is None:677            url_auth = get_auth_from_url(cast(str, self.url))678            auth = url_auth if any(url_auth) else None679 680        if auth:681            if isinstance(auth, tuple) and len(auth) == 2:  # type: ignore[arg-type]  # pyright widens tuple from Callable in AuthType682                # special-case basic HTTP auth683                auth_handler = HTTPBasicAuth(*auth)  # type: ignore[arg-type]  # pyright widens tuple from Callable in AuthType684            else:685                # TODO: can be fixed by flipping the conditionals686                auth_handler = cast("Callable[..., PreparedRequest]", auth)687 688            # Allow auth to make its changes.689            r = auth_handler(self)690 691            # Update self to reflect the auth changes.692            self.__dict__.update(r.__dict__)693 694            # Recompute Content-Length695            self.prepare_content_length(self.body)696 697    def prepare_cookies(698        self, cookies: RequestsCookieJar | CookieJar | dict[str, str] | None699    ) -> None:700        """Prepares the given HTTP cookie data.701 702        This function eventually generates a ``Cookie`` header from the703        given cookies using cookielib. Due to cookielib's design, the header704        will not be regenerated if it already exists, meaning this function705        can only be called once for the life of the706        :class:`PreparedRequest <PreparedRequest>` object. Any subsequent calls707        to ``prepare_cookies`` will have no actual effect, unless the "Cookie"708        header is removed beforehand.709        """710        if isinstance(cookies, cookielib.CookieJar):711            self._cookies = cookies712        else:713            self._cookies = cookiejar_from_dict(cookies)714 715        cookies_jar = cast("CookieJar", self._cookies)716        cookie_header = get_cookie_header(cookies_jar, self)717        if cookie_header is not None:718            self.headers["Cookie"] = cookie_header719 720    def prepare_hooks(self, hooks: _t.HooksInputType | None) -> None:721        """Prepares the given hooks."""722        # hooks can be passed as None to the prepare method and to this723        # method. To prevent iterating over None, simply use an empty list724        # if hooks is False-y725        hooks = hooks or {}726        for event in hooks:727            self.register_hook(event, hooks[event])728 729 730class Response:731    """The :class:`Response <Response>` object, which contains a732    server's response to an HTTP request.733    """734 735    _content: bytes | Literal[False] | None736    _content_consumed: bool737    _next: PreparedRequest | None738    status_code: int739    headers: CaseInsensitiveDict[str]740    raw: Any741    url: str742    encoding: str | None743    history: list[Response]744    reason: str | None745    cookies: RequestsCookieJar746    elapsed: datetime.timedelta747    request: PreparedRequest748    connection: HTTPAdapter749 750    __attrs__: list[str] = [751        "_content",752        "status_code",753        "headers",754        "url",755        "history",756        "encoding",757        "reason",758        "cookies",759        "elapsed",760        "request",761    ]762 763    def __init__(self) -> None:764        self._content = False765        self._content_consumed = False766        self._next = None767 768        #: Integer Code of responded HTTP Status, e.g. 404 or 200.769        self.status_code = None  # type: ignore[assignment]770 771        #: Case-insensitive Dictionary of Response Headers.772        #: For example, ``headers['content-encoding']`` will return the773        #: value of a ``'Content-Encoding'`` response header.774        self.headers = CaseInsensitiveDict()775 776        #: File-like object representation of response (for advanced usage).777        #: Use of ``raw`` requires that ``stream=True`` be set on the request.778        #: This requirement does not apply for use internally to Requests.779        self.raw = None780 781        #: Final URL location of Response.782        self.url = None  # type: ignore[assignment]783 784        #: Encoding to decode with when accessing r.text.785        self.encoding = None786 787        #: A list of :class:`Response <Response>` objects from788        #: the history of the Request. Any redirect responses will end789        #: up here. The list is sorted from the oldest to the most recent request.790        self.history = []791 792        #: Textual reason of responded HTTP Status, e.g. "Not Found" or "OK".793        self.reason = None794 795        #: A CookieJar of Cookies the server sent back.796        self.cookies = cookiejar_from_dict({})797 798        #: The amount of time elapsed between sending the request799        #: and the arrival of the response (as a timedelta).800        #: This property specifically measures the time taken between sending801        #: the first byte of the request and finishing parsing the headers. It802        #: is therefore unaffected by consuming the response content or the803        #: value of the ``stream`` keyword argument.804        self.elapsed = datetime.timedelta(0)805 806        #: The :class:`PreparedRequest <PreparedRequest>` object to which this807        #: is a response.808        self.request = None  # type: ignore[assignment]809 810    def __enter__(self) -> Self:811        return self812 813    def __exit__(self, *args: Any) -> None:814        self.close()815 816    def __getstate__(self) -> dict[str, Any]:817        # Consume everything; accessing the content attribute makes818        # sure the content has been fully read.819        if not self._content_consumed:820            self.content821 822        return {attr: getattr(self, attr, None) for attr in self.__attrs__}823 824    def __setstate__(self, state: dict[str, Any]) -> None:825        for name, value in state.items():826            setattr(self, name, value)827 828        # pickled objects do not have .raw829        setattr(self, "_content_consumed", True)830        setattr(self, "raw", None)831 832    def __repr__(self) -> str:833        return f"<Response [{self.status_code}]>"834 835    def __bool__(self) -> bool:836        """Returns True if :attr:`status_code` is less than 400.837 838        This attribute checks if the status code of the response is between839        400 and 600 to see if there was a client error or a server error. If840        the status code, is between 200 and 400, this will return True. This841        is **not** a check to see if the response code is ``200 OK``.842        """843        return self.ok844 845    def __nonzero__(self) -> bool:846        """Returns True if :attr:`status_code` is less than 400.847 848        This attribute checks if the status code of the response is between849        400 and 600 to see if there was a client error or a server error. If850        the status code, is between 200 and 400, this will return True. This851        is **not** a check to see if the response code is ``200 OK``.852        """853        return self.ok854 855    def __iter__(self) -> Iterator[bytes]:856        """Allows you to use a response as an iterator."""857        return self.iter_content(128)858 859    @property860    def ok(self) -> bool:861        """Returns True if :attr:`status_code` is less than 400, False if not.862 863        This attribute checks if the status code of the response is between864        400 and 600 to see if there was a client error or a server error. If865        the status code is between 200 and 400, this will return True. This866        is **not** a check to see if the response code is ``200 OK``.867        """868        try:869            self.raise_for_status()870        except HTTPError:871            return False872        return True873 874    @property875    def is_redirect(self) -> bool:876        """True if this Response is a well-formed HTTP redirect that could have877        been processed automatically (by :meth:`Session.resolve_redirects`).878        """879        return "location" in self.headers and self.status_code in REDIRECT_STATI880 881    @property882    def is_permanent_redirect(self) -> bool:883        """True if this Response one of the permanent versions of redirect."""884        return "location" in self.headers and self.status_code in (885            codes.moved_permanently,886            codes.permanent_redirect,887        )888 889    @property890    def next(self) -> PreparedRequest | None:891        """Returns a PreparedRequest for the next request in a redirect chain, if there is one."""892        return self._next893 894    @property895    def apparent_encoding(self) -> str | None:896        """The apparent encoding, provided by the charset_normalizer or chardet libraries."""897        if chardet is not None:898            return chardet.detect(self.content)["encoding"]899        else:900            # If no character detection library is available, we'll fall back901            # to a standard Python utf-8 str.902            return "utf-8"903 904    @overload905    def iter_content(906        self, chunk_size: int | None = 1, decode_unicode: Literal[False] = False907    ) -> Iterator[bytes]: ...908    @overload909    def iter_content(910        self, chunk_size: int | None = 1, *, decode_unicode: Literal[True]911    ) -> Iterator[str | bytes]: ...912    def iter_content(913        self, chunk_size: int | None = 1, decode_unicode: bool = False914    ) -> Iterator[str | bytes]:915        """Iterates over the response data.  When stream=True is set on the916        request, this avoids reading the content at once into memory for917        large responses.  The chunk size is the number of bytes it should918        read into memory.  This is not necessarily the length of each item919        returned as decoding can take place.920 921        chunk_size must be of type int or None. A value of None will922        function differently depending on the value of `stream`.923        stream=True will read data as it arrives in whatever size the924        chunks are received. If stream=False, data is returned as925        a single chunk.926 927        If decode_unicode is True, content will be decoded using encoding928        information from the response. If no encoding information is available,929        bytes will be returned. This can be bypassed by manually setting930        `encoding` on the response.931        """932 933        def generate() -> Generator[bytes, None, None]:934            # Special case for urllib3.935            if hasattr(self.raw, "stream"):936                try:937                    yield from self.raw.stream(chunk_size, decode_content=True)938                except ProtocolError as e:939                    raise ChunkedEncodingError(e)940                except DecodeError as e:941                    raise ContentDecodingError(e)942                except ReadTimeoutError as e:943                    raise ConnectionError(e)944                except SSLError as e:945                    raise RequestsSSLError(e)946            else:947                # Standard file-like object.948                while True:949                    chunk = self.raw.read(chunk_size)950                    if not chunk:951                        break952                    yield chunk953 954            self._content_consumed = True955 956        if self._content_consumed and isinstance(self._content, bool):957            raise StreamConsumedError()958        elif chunk_size is not None and not isinstance(chunk_size, int):  # type: ignore[reportUnnecessaryIsInstance]  # runtime guard for untyped callers959            raise TypeError(960                f"chunk_size must be an int, it is instead a {type(chunk_size)}."961            )962 963        if self._content_consumed:964            # simulate reading small chunks of the content965            content = cast(bytes, self._content)966            chunks = iter_slices(content, chunk_size)967        else:968            chunks = generate()969 970        if decode_unicode:971            chunks = stream_decode_response_unicode(chunks, self)972 973        return chunks974 975    @overload976    def iter_lines(977        self,978        chunk_size: int = ITER_CHUNK_SIZE,979        decode_unicode: Literal[False] = False,980        delimiter: bytes | None = None,981    ) -> Iterator[bytes]: ...982    @overload983    def iter_lines(984        self,985        chunk_size: int = ITER_CHUNK_SIZE,986        *,987        decode_unicode: Literal[True],988        delimiter: str | bytes | None = None,989    ) -> Iterator[str | bytes]: ...990    def iter_lines(991        self,992        chunk_size: int = ITER_CHUNK_SIZE,993        decode_unicode: bool = False,994        delimiter: str | bytes | None = None,995    ) -> Iterator[str | bytes]:996        """Iterates over the response data, one line at a time.  When997        stream=True is set on the request, this avoids reading the998        content at once into memory for large responses.999 1000        The decode_unicode param works the same as in `iter_content`, with the1001        same caveats.1002 1003        .. note:: This method is not reentrant safe.1004        """1005 1006        pending: str | bytes | None = None1007 1008        for chunk in self.iter_content(1009            chunk_size=chunk_size, decode_unicode=decode_unicode1010        ):1011            if pending is not None:1012                # TODO: remove cast after iter_lines rewrite1013                chunk = cast("str | bytes", pending + chunk)  # type: ignore[operator]1014 1015            if delimiter:1016                lines = chunk.split(delimiter)  # type: ignore[arg-type]1017            else:1018                lines = chunk.splitlines()1019 1020            if lines and lines[-1] and chunk and lines[-1][-1] == chunk[-1]:1021                pending = lines.pop()1022            else:1023                pending = None1024 1025            yield from lines1026 1027        if pending is not None:1028            yield pending1029 1030    @property1031    def content(self) -> bytes:1032        """Content of the response, in bytes."""1033 1034        if self._content is False:1035            # Read the contents.1036            if self._content_consumed:1037                raise RuntimeError("The content for this response was already consumed")1038 1039            if self.status_code == 0 or self.raw is None:1040                self._content = None1041            else:1042                self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""1043 1044        self._content_consumed = True1045        # don't need to release the connection; that's been handled by urllib31046        # since we exhausted the data.1047        return self._content  # type: ignore[return-value]1048 1049    @property1050    def text(self) -> str:1051        """Content of the response, in unicode.1052 1053        If Response.encoding is None, encoding will be guessed using1054        ``charset_normalizer`` or ``chardet``.1055 1056        The encoding of the response content is determined based solely on HTTP1057        headers, following RFC 2616 to the letter. If you can take advantage of1058        non-HTTP knowledge to make a better guess at the encoding, you should1059        set ``r.encoding`` appropriately before accessing this property.1060        """1061 1062        # Try charset from content-type1063        content = None1064        encoding = self.encoding1065 1066        if not self.content:1067            return ""1068 1069        # Fallback to auto-detected encoding.1070        if self.encoding is None:1071            encoding = self.apparent_encoding1072 1073        # Decode unicode from given encoding.1074        try:1075            content = str(self.content, encoding or "utf-8", errors="replace")1076        except (LookupError, TypeError):1077            # A LookupError is raised if the encoding was not found which could1078            # indicate a misspelling or similar mistake.1079            #1080            # A TypeError can be raised if encoding is None1081            #1082            # So we try blindly encoding.1083            content = str(self.content, errors="replace")1084 1085        return content1086 1087    def json(self, **kwargs: Any) -> Any:1088        r"""Decodes the JSON response body (if any) as a Python object.1089 1090        This may return a dictionary, list, etc. depending on what is in the response.1091 1092        :param \*\*kwargs: Optional arguments that ``json.loads`` takes.1093        :raises requests.exceptions.JSONDecodeError: If the response body does not1094            contain valid json.1095        """1096 1097        if not self.encoding and self.content and len(self.content) > 3:1098            # No encoding set. JSON RFC 4627 section 3 states we should expect1099            # UTF-8, -16 or -32. Detect which one to use; If the detection or1100            # decoding fails, fall back to `self.text` (using charset_normalizer to make1101            # a best guess).1102            encoding = guess_json_utf(self.content)1103            if encoding is not None:1104                try:1105                    return complexjson.loads(self.content.decode(encoding), **kwargs)1106                except UnicodeDecodeError:1107                    # Wrong UTF codec detected; usually because it's not UTF-81108                    # but some other 8-bit codec.  This is an RFC violation,1109                    # and the server didn't bother to tell us what codec *was*1110                    # used.1111                    pass1112                except JSONDecodeError as e:1113                    raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)1114 1115        try:1116            return complexjson.loads(self.text, **kwargs)1117        except JSONDecodeError as e:1118            # Catch JSON-related errors and raise as requests.JSONDecodeError1119            # This aliases json.JSONDecodeError and simplejson.JSONDecodeError1120            raise RequestsJSONDecodeError(e.msg, e.doc, e.pos)1121 1122    @property1123    def links(self) -> dict[str, dict[str, str]]:1124        """Returns the parsed header links of the response, if any."""1125 1126        header = self.headers.get("link")1127 1128        resolved_links: dict[str, dict[str, str]] = {}1129 1130        if header:1131            links = parse_header_links(header)1132 1133            for link in links:1134                key = link.get("rel") or link.get("url")1135                if key is not None:1136                    resolved_links[key] = link1137 1138        return resolved_links1139 1140    def raise_for_status(self) -> None:1141        """Raises :class:`HTTPError`, if one occurred."""1142 1143        http_error_msg = ""1144        if isinstance(self.reason, bytes):1145            # We attempt to decode utf-8 first because some servers1146            # choose to localize their reason strings. If the string1147            # isn't utf-8, we fall back to iso-8859-1 for all other1148            # encodings. (See PR #3538)1149            try:1150                reason = self.reason.decode("utf-8")1151            except UnicodeDecodeError:1152                reason = self.reason.decode("iso-8859-1")1153        else:1154            reason = self.reason1155 1156        if 400 <= self.status_code < 500:1157            http_error_msg = (1158                f"{self.status_code} Client Error: {reason} for url: {self.url}"1159            )1160 1161        elif 500 <= self.status_code < 600:1162            http_error_msg = (1163                f"{self.status_code} Server Error: {reason} for url: {self.url}"1164            )1165 1166        if http_error_msg:1167            raise HTTPError(http_error_msg, response=self)1168 1169    def close(self) -> None:1170        """Releases the connection back to the pool. Once this method has been1171        called the underlying ``raw`` object must not be accessed again.1172 1173        *Note: Should not normally need to be called explicitly.*1174        """1175        if not self._content_consumed:1176            self.raw.close()1177 1178        release_conn = getattr(self.raw, "release_conn", None)1179        if release_conn is not None:1180            release_conn()1181 
codekingpro/portable-devtools · Team Ai