Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
request.py651 linesDownload Raw Back to wrappers
1from __future__ import annotations2 3import collections.abc as cabc4import functools5import json6import typing as t7from io import BytesIO8 9from .._internal import _wsgi_decoding_dance10from ..datastructures import CombinedMultiDict11from ..datastructures import EnvironHeaders12from ..datastructures import FileStorage13from ..datastructures import ImmutableMultiDict14from ..datastructures import iter_multi_items15from ..datastructures import MultiDict16from ..exceptions import BadRequest17from ..exceptions import UnsupportedMediaType18from ..formparser import default_stream_factory19from ..formparser import FormDataParser20from ..sansio.request import Request as _SansIORequest21from ..utils import cached_property22from ..utils import environ_property23from ..wsgi import _get_server24from ..wsgi import get_input_stream25 26if t.TYPE_CHECKING:27    from _typeshed.wsgi import WSGIApplication28    from _typeshed.wsgi import WSGIEnvironment29 30 31class Request(_SansIORequest):32    """Represents an incoming WSGI HTTP request, with headers and body33    taken from the WSGI environment. Has properties and methods for34    using the functionality defined by various HTTP specs. The data in35    requests object is read-only.36 37    Text data is assumed to use UTF-8 encoding, which should be true for38    the vast majority of modern clients. Using an encoding set by the39    client is unsafe in Python due to extra encodings it provides, such40    as ``zip``. To change the assumed encoding, subclass and replace41    :attr:`charset`.42 43    :param environ: The WSGI environ is generated by the WSGI server and44        contains information about the server configuration and client45        request.46    :param populate_request: Add this request object to the WSGI environ47        as ``environ['werkzeug.request']``. Can be useful when48        debugging.49    :param shallow: Makes reading from :attr:`stream` (and any method50        that would read from it) raise a :exc:`RuntimeError`. Useful to51        prevent consuming the form data in middleware, which would make52        it unavailable to the final application.53 54    .. versionchanged:: 3.055        The ``charset``, ``url_charset``, and ``encoding_errors`` parameters56        were removed.57 58    .. versionchanged:: 2.159        Old ``BaseRequest`` and mixin classes were removed.60 61    .. versionchanged:: 2.162        Remove the ``disable_data_descriptor`` attribute.63 64    .. versionchanged:: 2.065        Combine ``BaseRequest`` and mixins into a single ``Request``66        class.67 68    .. versionchanged:: 0.569        Read-only mode is enforced with immutable classes for all data.70    """71 72    #: the maximum content length.  This is forwarded to the form data73    #: parsing function (:func:`parse_form_data`).  When set and the74    #: :attr:`form` or :attr:`files` attribute is accessed and the75    #: parsing fails because more than the specified value is transmitted76    #: a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised.77    #:78    #: .. versionadded:: 0.579    max_content_length: int | None = None80 81    #: the maximum form field size.  This is forwarded to the form data82    #: parsing function (:func:`parse_form_data`).  When set and the83    #: :attr:`form` or :attr:`files` attribute is accessed and the84    #: data in memory for post data is longer than the specified value a85    #: :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception is raised.86    #:87    #: .. versionchanged:: 3.188    #:     Defaults to 500kB instead of unlimited.89    #:90    #: .. versionadded:: 0.591    max_form_memory_size: int | None = 500_00092 93    #: The maximum number of multipart parts to parse, passed to94    #: :attr:`form_data_parser_class`. Parsing form data with more than this95    #: many parts will raise :exc:`~.RequestEntityTooLarge`.96    #:97    #: .. versionadded:: 2.2.398    max_form_parts = 100099 100    #: The form data parser that should be used.  Can be replaced to customize101    #: the form date parsing.102    form_data_parser_class: type[FormDataParser] = FormDataParser103 104    #: The WSGI environment containing HTTP headers and information from105    #: the WSGI server.106    environ: WSGIEnvironment107 108    #: Set when creating the request object. If ``True``, reading from109    #: the request body will cause a ``RuntimeException``. Useful to110    #: prevent modifying the stream from middleware.111    shallow: bool112 113    def __init__(114        self,115        environ: WSGIEnvironment,116        populate_request: bool = True,117        shallow: bool = False,118    ) -> None:119        super().__init__(120            method=environ.get("REQUEST_METHOD", "GET"),121            scheme=environ.get("wsgi.url_scheme", "http"),122            server=_get_server(environ),123            root_path=_wsgi_decoding_dance(environ.get("SCRIPT_NAME") or ""),124            path=_wsgi_decoding_dance(environ.get("PATH_INFO") or ""),125            query_string=environ.get("QUERY_STRING", "").encode("latin1"),126            headers=EnvironHeaders(environ),127            remote_addr=environ.get("REMOTE_ADDR"),128        )129        self.environ = environ130        self.shallow = shallow131 132        if populate_request and not shallow:133            self.environ["werkzeug.request"] = self134 135    @classmethod136    def from_values(cls, *args: t.Any, **kwargs: t.Any) -> Request:137        """Create a new request object based on the values provided.  If138        environ is given missing values are filled from there.  This method is139        useful for small scripts when you need to simulate a request from an URL.140        Do not use this method for unittesting, there is a full featured client141        object (:class:`Client`) that allows to create multipart requests,142        support for cookies etc.143 144        This accepts the same options as the145        :class:`~werkzeug.test.EnvironBuilder`.146 147        .. versionchanged:: 0.5148           This method now accepts the same arguments as149           :class:`~werkzeug.test.EnvironBuilder`.  Because of this the150           `environ` parameter is now called `environ_overrides`.151 152        :return: request object153        """154        from ..test import EnvironBuilder155 156        builder = EnvironBuilder(*args, **kwargs)157        try:158            return builder.get_request(cls)159        finally:160            builder.close()161 162    @classmethod163    def application(cls, f: t.Callable[[Request], WSGIApplication]) -> WSGIApplication:164        """Decorate a function as responder that accepts the request as165        the last argument.  This works like the :func:`responder`166        decorator but the function is passed the request object as the167        last argument and the request object will be closed168        automatically::169 170            @Request.application171            def my_wsgi_app(request):172                return Response('Hello World!')173 174        As of Werkzeug 0.14 HTTP exceptions are automatically caught and175        converted to responses instead of failing.176 177        :param f: the WSGI callable to decorate178        :return: a new WSGI callable179        """180        #: return a callable that wraps the -2nd argument with the request181        #: and calls the function with all the arguments up to that one and182        #: the request.  The return value is then called with the latest183        #: two arguments.  This makes it possible to use this decorator for184        #: both standalone WSGI functions as well as bound methods and185        #: partially applied functions.186        from ..exceptions import HTTPException187 188        @functools.wraps(f)189        def application(*args: t.Any) -> cabc.Iterable[bytes]:190            request = cls(args[-2])191            with request:192                try:193                    resp = f(*args[:-2] + (request,))194                except HTTPException as e:195                    resp = t.cast("WSGIApplication", e.get_response(args[-2]))196                return resp(*args[-2:])197 198        return t.cast("WSGIApplication", application)199 200    def _get_file_stream(201        self,202        total_content_length: int | None,203        content_type: str | None,204        filename: str | None = None,205        content_length: int | None = None,206    ) -> t.IO[bytes]:207        """Called to get a stream for the file upload.208 209        This must provide a file-like class with `read()`, `readline()`210        and `seek()` methods that is both writeable and readable.211 212        The default implementation returns a temporary file if the total213        content length is higher than 500KB.  Because many browsers do not214        provide a content length for the files only the total content215        length matters.216 217        :param total_content_length: the total content length of all the218                                     data in the request combined.  This value219                                     is guaranteed to be there.220        :param content_type: the mimetype of the uploaded file.221        :param filename: the filename of the uploaded file.  May be `None`.222        :param content_length: the length of this file.  This value is usually223                               not provided because webbrowsers do not provide224                               this value.225        """226        return default_stream_factory(227            total_content_length=total_content_length,228            filename=filename,229            content_type=content_type,230            content_length=content_length,231        )232 233    @property234    def want_form_data_parsed(self) -> bool:235        """``True`` if the request method carries content. By default236        this is true if a ``Content-Type`` is sent.237 238        .. versionadded:: 0.8239        """240        return bool(self.environ.get("CONTENT_TYPE"))241 242    def make_form_data_parser(self) -> FormDataParser:243        """Creates the form data parser. Instantiates the244        :attr:`form_data_parser_class` with some parameters.245 246        .. versionadded:: 0.8247        """248        return self.form_data_parser_class(249            stream_factory=self._get_file_stream,250            max_form_memory_size=self.max_form_memory_size,251            max_content_length=self.max_content_length,252            max_form_parts=self.max_form_parts,253            cls=self.parameter_storage_class,254        )255 256    def _load_form_data(self) -> None:257        """Method used internally to retrieve submitted data.  After calling258        this sets `form` and `files` on the request object to multi dicts259        filled with the incoming form data.  As a matter of fact the input260        stream will be empty afterwards.  You can also call this method to261        force the parsing of the form data.262 263        .. versionadded:: 0.8264        """265        # abort early if we have already consumed the stream266        if "form" in self.__dict__:267            return268 269        if self.want_form_data_parsed:270            parser = self.make_form_data_parser()271            data = parser.parse(272                self._get_stream_for_parsing(),273                self.mimetype,274                self.content_length,275                self.mimetype_params,276            )277        else:278            data = (279                self.stream,280                self.parameter_storage_class(),281                self.parameter_storage_class(),282            )283 284        # inject the values into the instance dict so that we bypass285        # our cached_property non-data descriptor.286        d = self.__dict__287        d["stream"], d["form"], d["files"] = data288 289    def _get_stream_for_parsing(self) -> t.IO[bytes]:290        """This is the same as accessing :attr:`stream` with the difference291        that if it finds cached data from calling :meth:`get_data` first it292        will create a new stream out of the cached data.293 294        .. versionadded:: 0.9.3295        """296        cached_data = getattr(self, "_cached_data", None)297        if cached_data is not None:298            return BytesIO(cached_data)299        return self.stream300 301    def close(self) -> None:302        """Closes associated resources of this request object.  This303        closes all file handles explicitly.  You can also use the request304        object in a with statement which will automatically close it.305 306        .. versionadded:: 0.9307        """308        files = self.__dict__.get("files")309        for _key, value in iter_multi_items(files or ()):310            value.close()311 312    def __enter__(self) -> Request:313        return self314 315    def __exit__(self, exc_type, exc_value, tb) -> None:  # type: ignore316        self.close()317 318    @cached_property319    def stream(self) -> t.IO[bytes]:320        """The WSGI input stream, with safety checks. This stream can only be consumed321        once.322 323        Use :meth:`get_data` to get the full data as bytes or text. The :attr:`data`324        attribute will contain the full bytes only if they do not represent form data.325        The :attr:`form` attribute will contain the parsed form data in that case.326 327        Unlike :attr:`input_stream`, this stream guards against infinite streams or328        reading past :attr:`content_length` or :attr:`max_content_length`.329 330        If ``max_content_length`` is set, it can be enforced on streams if331        ``wsgi.input_terminated`` is set. Otherwise, an empty stream is returned.332 333        If the limit is reached before the underlying stream is exhausted (such as a334        file that is too large, or an infinite stream), the remaining contents of the335        stream cannot be read safely. Depending on how the server handles this, clients336        may show a "connection reset" failure instead of seeing the 413 response.337 338        .. versionchanged:: 2.3339            Check ``max_content_length`` preemptively and while reading.340 341        .. versionchanged:: 0.9342            The stream is always set (but may be consumed) even if form parsing was343            accessed first.344        """345        if self.shallow:346            raise RuntimeError(347                "This request was created with 'shallow=True', reading"348                " from the input stream is disabled."349            )350 351        return get_input_stream(352            self.environ, max_content_length=self.max_content_length353        )354 355    input_stream = environ_property[t.IO[bytes]](356        "wsgi.input",357        doc="""The raw WSGI input stream, without any safety checks.358 359        This is dangerous to use. It does not guard against infinite streams or reading360        past :attr:`content_length` or :attr:`max_content_length`.361 362        Use :attr:`stream` instead.363        """,364    )365 366    @cached_property367    def data(self) -> bytes:368        """The raw data read from :attr:`stream`. Will be empty if the request369        represents form data.370 371        To get the raw data even if it represents form data, use :meth:`get_data`.372        """373        return self.get_data(parse_form_data=True)374 375    @t.overload376    def get_data(377        self,378        cache: bool = True,379        as_text: t.Literal[False] = False,380        parse_form_data: bool = False,381    ) -> bytes: ...382 383    @t.overload384    def get_data(385        self,386        cache: bool = True,387        as_text: t.Literal[True] = ...,388        parse_form_data: bool = False,389    ) -> str: ...390 391    def get_data(392        self, cache: bool = True, as_text: bool = False, parse_form_data: bool = False393    ) -> bytes | str:394        """This reads the buffered incoming data from the client into one395        bytes object.  By default this is cached but that behavior can be396        changed by setting `cache` to `False`.397 398        Usually it's a bad idea to call this method without checking the399        content length first as a client could send dozens of megabytes or more400        to cause memory problems on the server.401 402        Note that if the form data was already parsed this method will not403        return anything as form data parsing does not cache the data like404        this method does.  To implicitly invoke form data parsing function405        set `parse_form_data` to `True`.  When this is done the return value406        of this method will be an empty string if the form parser handles407        the data.  This generally is not necessary as if the whole data is408        cached (which is the default) the form parser will used the cached409        data to parse the form data.  Please be generally aware of checking410        the content length first in any case before calling this method411        to avoid exhausting server memory.412 413        If `as_text` is set to `True` the return value will be a decoded414        string.415 416        .. versionadded:: 0.9417        """418        rv = getattr(self, "_cached_data", None)419        if rv is None:420            if parse_form_data:421                self._load_form_data()422            rv = self.stream.read()423            if cache:424                self._cached_data = rv425        if as_text:426            rv = rv.decode(errors="replace")427        return rv428 429    @cached_property430    def form(self) -> ImmutableMultiDict[str, str]:431        """The form parameters.  By default an432        :class:`~werkzeug.datastructures.ImmutableMultiDict`433        is returned from this function.  This can be changed by setting434        :attr:`parameter_storage_class` to a different type.  This might435        be necessary if the order of the form data is important.436 437        Please keep in mind that file uploads will not end up here, but instead438        in the :attr:`files` attribute.439 440        .. versionchanged:: 0.9441 442            Previous to Werkzeug 0.9 this would only contain form data for POST443            and PUT requests.444        """445        self._load_form_data()446        return self.form447 448    @cached_property449    def values(self) -> CombinedMultiDict[str, str]:450        """A :class:`werkzeug.datastructures.CombinedMultiDict` that451        combines :attr:`args` and :attr:`form`.452 453        For GET requests, only ``args`` are present, not ``form``.454 455        .. versionchanged:: 2.0456            For GET requests, only ``args`` are present, not ``form``.457        """458        sources = [self.args]459 460        if self.method != "GET":461            # GET requests can have a body, and some caching proxies462            # might not treat that differently than a normal GET463            # request, allowing form data to "invisibly" affect the464            # cache without indication in the query string / URL.465            sources.append(self.form)466 467        args = []468 469        for d in sources:470            if not isinstance(d, MultiDict):471                d = MultiDict(d)472 473            args.append(d)474 475        return CombinedMultiDict(args)476 477    @cached_property478    def files(self) -> ImmutableMultiDict[str, FileStorage]:479        """:class:`~werkzeug.datastructures.MultiDict` object containing480        all uploaded files.  Each key in :attr:`files` is the name from the481        ``<input type="file" name="">``.  Each value in :attr:`files` is a482        Werkzeug :class:`~werkzeug.datastructures.FileStorage` object.483 484        It basically behaves like a standard file object you know from Python,485        with the difference that it also has a486        :meth:`~werkzeug.datastructures.FileStorage.save` function that can487        store the file on the filesystem.488 489        Note that :attr:`files` will only contain data if the request method was490        POST, PUT or PATCH and the ``<form>`` that posted to the request had491        ``enctype="multipart/form-data"``.  It will be empty otherwise.492 493        See the :class:`~werkzeug.datastructures.MultiDict` /494        :class:`~werkzeug.datastructures.FileStorage` documentation for495        more details about the used data structure.496        """497        self._load_form_data()498        return self.files499 500    @property501    def script_root(self) -> str:502        """Alias for :attr:`self.root_path`. ``environ["SCRIPT_NAME"]``503        without a trailing slash.504        """505        return self.root_path506 507    @cached_property508    def url_root(self) -> str:509        """Alias for :attr:`root_url`. The URL with scheme, host, and510        root path. For example, ``https://example.com/app/``.511        """512        return self.root_url513 514    remote_user = environ_property[str](515        "REMOTE_USER",516        doc="""If the server supports user authentication, and the517        script is protected, this attribute contains the username the518        user has authenticated as.""",519    )520    is_multithread = environ_property[bool](521        "wsgi.multithread",522        doc="""boolean that is `True` if the application is served by a523        multithreaded WSGI server.""",524    )525    is_multiprocess = environ_property[bool](526        "wsgi.multiprocess",527        doc="""boolean that is `True` if the application is served by a528        WSGI server that spawns multiple processes.""",529    )530    is_run_once = environ_property[bool](531        "wsgi.run_once",532        doc="""boolean that is `True` if the application will be533        executed only once in a process lifetime.  This is the case for534        CGI for example, but it's not guaranteed that the execution only535        happens one time.""",536    )537 538    # JSON539 540    #: A module or other object that has ``dumps`` and ``loads``541    #: functions that match the API of the built-in :mod:`json` module.542    json_module = json543 544    @property545    def json(self) -> t.Any:546        """The parsed JSON data if :attr:`mimetype` indicates JSON547        (:mimetype:`application/json`, see :attr:`is_json`).548 549        Calls :meth:`get_json` with default arguments.550 551        If the request content type is not ``application/json``, this552        will raise a 415 Unsupported Media Type error.553 554        .. versionchanged:: 2.3555            Raise a 415 error instead of 400.556 557        .. versionchanged:: 2.1558            Raise a 400 error if the content type is incorrect.559        """560        return self.get_json()561 562    # Cached values for ``(silent=False, silent=True)``. Initialized563    # with sentinel values.564    _cached_json: tuple[t.Any, t.Any] = (Ellipsis, Ellipsis)565 566    @t.overload567    def get_json(568        self, force: bool = ..., silent: t.Literal[False] = ..., cache: bool = ...569    ) -> t.Any: ...570 571    @t.overload572    def get_json(573        self, force: bool = ..., silent: bool = ..., cache: bool = ...574    ) -> t.Any | None: ...575 576    def get_json(577        self, force: bool = False, silent: bool = False, cache: bool = True578    ) -> t.Any | None:579        """Parse :attr:`data` as JSON.580 581        If the mimetype does not indicate JSON582        (:mimetype:`application/json`, see :attr:`is_json`), or parsing583        fails, :meth:`on_json_loading_failed` is called and584        its return value is used as the return value. By default this585        raises a 415 Unsupported Media Type resp.586 587        :param force: Ignore the mimetype and always try to parse JSON.588        :param silent: Silence mimetype and parsing errors, and589            return ``None`` instead.590        :param cache: Store the parsed JSON to return for subsequent591            calls.592 593        .. versionchanged:: 2.3594            Raise a 415 error instead of 400.595 596        .. versionchanged:: 2.1597            Raise a 400 error if the content type is incorrect.598        """599        if cache and self._cached_json[silent] is not Ellipsis:600            return self._cached_json[silent]601 602        if not (force or self.is_json):603            if not silent:604                return self.on_json_loading_failed(None)605            else:606                return None607 608        data = self.get_data(cache=cache)609 610        try:611            rv = self.json_module.loads(data)612        except ValueError as e:613            if silent:614                rv = None615 616                if cache:617                    normal_rv, _ = self._cached_json618                    self._cached_json = (normal_rv, rv)619            else:620                rv = self.on_json_loading_failed(e)621 622                if cache:623                    _, silent_rv = self._cached_json624                    self._cached_json = (rv, silent_rv)625        else:626            if cache:627                self._cached_json = (rv, rv)628 629        return rv630 631    def on_json_loading_failed(self, e: ValueError | None) -> t.Any:632        """Called if :meth:`get_json` fails and isn't silenced.633 634        If this method returns a value, it is used as the return value635        for :meth:`get_json`. The default implementation raises636        :exc:`~werkzeug.exceptions.BadRequest`.637 638        :param e: If parsing failed, this is the exception. It will be639            ``None`` if the content type wasn't ``application/json``.640 641        .. versionchanged:: 2.3642            Raise a 415 error instead of 400.643        """644        if e is not None:645            raise BadRequest(f"Failed to decode JSON object: {e}")646 647        raise UnsupportedMediaType(648            "Did not attempt to load JSON data because the request"649            " Content-Type was not 'application/json'."650        )651 
codekingpro/portable-devtools · Team Ai