123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- from __future__ import annotations
- import typing as t
- from werkzeug.exceptions import BadRequest
- from werkzeug.exceptions import HTTPException
- from werkzeug.wrappers import Request as RequestBase
- from werkzeug.wrappers import Response as ResponseBase
- from . import json
- from .globals import current_app
- from .helpers import _split_blueprint_path
- if t.TYPE_CHECKING: # pragma: no cover
- from werkzeug.routing import Rule
- class Request(RequestBase):
- """The request object used by default in Flask. Remembers the
- matched endpoint and view arguments.
- It is what ends up as :class:`~flask.request`. If you want to replace
- the request object used you can subclass this and set
- :attr:`~flask.Flask.request_class` to your subclass.
- The request object is a :class:`~werkzeug.wrappers.Request` subclass and
- provides all of the attributes Werkzeug defines plus a few Flask
- specific ones.
- """
- json_module: t.Any = json
- #: The internal URL rule that matched the request. This can be
- #: useful to inspect which methods are allowed for the URL from
- #: a before/after handler (``request.url_rule.methods``) etc.
- #: Though if the request's method was invalid for the URL rule,
- #: the valid list is available in ``routing_exception.valid_methods``
- #: instead (an attribute of the Werkzeug exception
- #: :exc:`~werkzeug.exceptions.MethodNotAllowed`)
- #: because the request was never internally bound.
- #:
- #: .. versionadded:: 0.6
- url_rule: Rule | None = None
- #: A dict of view arguments that matched the request. If an exception
- #: happened when matching, this will be ``None``.
- view_args: dict[str, t.Any] | None = None
- #: If matching the URL failed, this is the exception that will be
- #: raised / was raised as part of the request handling. This is
- #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
- #: something similar.
- routing_exception: HTTPException | None = None
- _max_content_length: int | None = None
- _max_form_memory_size: int | None = None
- _max_form_parts: int | None = None
- @property
- def max_content_length(self) -> int | None:
- """The maximum number of bytes that will be read during this request. If
- this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`
- error is raised. If it is set to ``None``, no limit is enforced at the
- Flask application level. However, if it is ``None`` and the request has
- no ``Content-Length`` header and the WSGI server does not indicate that
- it terminates the stream, then no data is read to avoid an infinite
- stream.
- Each request defaults to the :data:`MAX_CONTENT_LENGTH` config, which
- defaults to ``None``. It can be set on a specific ``request`` to apply
- the limit to that specific view. This should be set appropriately based
- on an application's or view's specific needs.
- .. versionchanged:: 3.1
- This can be set per-request.
- .. versionchanged:: 0.6
- This is configurable through Flask config.
- """
- if self._max_content_length is not None:
- return self._max_content_length
- if not current_app:
- return super().max_content_length
- return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return]
- @max_content_length.setter
- def max_content_length(self, value: int | None) -> None:
- self._max_content_length = value
- @property
- def max_form_memory_size(self) -> int | None:
- """The maximum size in bytes any non-file form field may be in a
- ``multipart/form-data`` body. If this limit is exceeded, a 413
- :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
- is set to ``None``, no limit is enforced at the Flask application level.
- Each request defaults to the :data:`MAX_FORM_MEMORY_SIZE` config, which
- defaults to ``500_000``. It can be set on a specific ``request`` to
- apply the limit to that specific view. This should be set appropriately
- based on an application's or view's specific needs.
- .. versionchanged:: 3.1
- This is configurable through Flask config.
- """
- if self._max_form_memory_size is not None:
- return self._max_form_memory_size
- if not current_app:
- return super().max_form_memory_size
- return current_app.config["MAX_FORM_MEMORY_SIZE"] # type: ignore[no-any-return]
- @max_form_memory_size.setter
- def max_form_memory_size(self, value: int | None) -> None:
- self._max_form_memory_size = value
- @property # type: ignore[override]
- def max_form_parts(self) -> int | None:
- """The maximum number of fields that may be present in a
- ``multipart/form-data`` body. If this limit is exceeded, a 413
- :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it
- is set to ``None``, no limit is enforced at the Flask application level.
- Each request defaults to the :data:`MAX_FORM_PARTS` config, which
- defaults to ``1_000``. It can be set on a specific ``request`` to apply
- the limit to that specific view. This should be set appropriately based
- on an application's or view's specific needs.
- .. versionchanged:: 3.1
- This is configurable through Flask config.
- """
- if self._max_form_parts is not None:
- return self._max_form_parts
- if not current_app:
- return super().max_form_parts
- return current_app.config["MAX_FORM_PARTS"] # type: ignore[no-any-return]
- @max_form_parts.setter
- def max_form_parts(self, value: int | None) -> None:
- self._max_form_parts = value
- @property
- def endpoint(self) -> str | None:
- """The endpoint that matched the request URL.
- This will be ``None`` if matching failed or has not been
- performed yet.
- This in combination with :attr:`view_args` can be used to
- reconstruct the same URL or a modified URL.
- """
- if self.url_rule is not None:
- return self.url_rule.endpoint # type: ignore[no-any-return]
- return None
- @property
- def blueprint(self) -> str | None:
- """The registered name of the current blueprint.
- This will be ``None`` if the endpoint is not part of a
- blueprint, or if URL matching failed or has not been performed
- yet.
- This does not necessarily match the name the blueprint was
- created with. It may have been nested, or registered with a
- different name.
- """
- endpoint = self.endpoint
- if endpoint is not None and "." in endpoint:
- return endpoint.rpartition(".")[0]
- return None
- @property
- def blueprints(self) -> list[str]:
- """The registered names of the current blueprint upwards through
- parent blueprints.
- This will be an empty list if there is no current blueprint, or
- if URL matching failed.
- .. versionadded:: 2.0.1
- """
- name = self.blueprint
- if name is None:
- return []
- return _split_blueprint_path(name)
- def _load_form_data(self) -> None:
- super()._load_form_data()
- # In debug mode we're replacing the files multidict with an ad-hoc
- # subclass that raises a different error for key errors.
- if (
- current_app
- and current_app.debug
- and self.mimetype != "multipart/form-data"
- and not self.files
- ):
- from .debughelpers import attach_enctype_error_multidict
- attach_enctype_error_multidict(self)
- def on_json_loading_failed(self, e: ValueError | None) -> t.Any:
- try:
- return super().on_json_loading_failed(e)
- except BadRequest as ebr:
- if current_app and current_app.debug:
- raise
- raise BadRequest() from ebr
- class Response(ResponseBase):
- """The response object that is used by default in Flask. Works like the
- response object from Werkzeug but is set to have an HTML mimetype by
- default. Quite often you don't have to create this object yourself because
- :meth:`~flask.Flask.make_response` will take care of that for you.
- If you want to replace the response object used you can subclass this and
- set :attr:`~flask.Flask.response_class` to your subclass.
- .. versionchanged:: 1.0
- JSON support is added to the response, like the request. This is useful
- when testing to get the test client response data as JSON.
- .. versionchanged:: 1.0
- Added :attr:`max_cookie_size`.
- """
- default_mimetype: str | None = "text/html"
- json_module = json
- autocorrect_location_header = False
- @property
- def max_cookie_size(self) -> int: # type: ignore
- """Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
- See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
- Werkzeug's docs.
- """
- if current_app:
- return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return]
- # return Werkzeug's default when not in an app context
- return super().max_cookie_size
|