codekingpro/portable-devtools
114k
1from __future__ import annotations2 3import typing as t4 5from werkzeug.exceptions import BadRequest6from werkzeug.exceptions import HTTPException7from werkzeug.wrappers import Request as RequestBase8from werkzeug.wrappers import Response as ResponseBase9 10from . import json11from .globals import current_app12from .helpers import _split_blueprint_path13 14if t.TYPE_CHECKING: # pragma: no cover15 from werkzeug.routing import Rule16 17 18class Request(RequestBase):19 """The request object used by default in Flask. Remembers the20 matched endpoint and view arguments.21 22 It is what ends up as :class:`~flask.request`. If you want to replace23 the request object used you can subclass this and set24 :attr:`~flask.Flask.request_class` to your subclass.25 26 The request object is a :class:`~werkzeug.wrappers.Request` subclass and27 provides all of the attributes Werkzeug defines plus a few Flask28 specific ones.29 """30 31 json_module: t.Any = json32 33 #: The internal URL rule that matched the request. This can be34 #: useful to inspect which methods are allowed for the URL from35 #: a before/after handler (``request.url_rule.methods``) etc.36 #: Though if the request's method was invalid for the URL rule,37 #: the valid list is available in ``routing_exception.valid_methods``38 #: instead (an attribute of the Werkzeug exception39 #: :exc:`~werkzeug.exceptions.MethodNotAllowed`)40 #: because the request was never internally bound.41 #:42 #: .. versionadded:: 0.643 url_rule: Rule | None = None44 45 #: A dict of view arguments that matched the request. If an exception46 #: happened when matching, this will be ``None``.47 view_args: dict[str, t.Any] | None = None48 49 #: If matching the URL failed, this is the exception that will be50 #: raised / was raised as part of the request handling. This is51 #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or52 #: something similar.53 routing_exception: HTTPException | None = None54 55 _max_content_length: int | None = None56 _max_form_memory_size: int | None = None57 _max_form_parts: int | None = None58 59 @property60 def max_content_length(self) -> int | None:61 """The maximum number of bytes that will be read during this request. If62 this limit is exceeded, a 413 :exc:`~werkzeug.exceptions.RequestEntityTooLarge`63 error is raised. If it is set to ``None``, no limit is enforced at the64 Flask application level. However, if it is ``None`` and the request has65 no ``Content-Length`` header and the WSGI server does not indicate that66 it terminates the stream, then no data is read to avoid an infinite67 stream.68 69 Each request defaults to the :data:`MAX_CONTENT_LENGTH` config, which70 defaults to ``None``. It can be set on a specific ``request`` to apply71 the limit to that specific view. This should be set appropriately based72 on an application's or view's specific needs.73 74 .. versionchanged:: 3.175 This can be set per-request.76 77 .. versionchanged:: 0.678 This is configurable through Flask config.79 """80 if self._max_content_length is not None:81 return self._max_content_length82 83 if not current_app:84 return super().max_content_length85 86 return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return]87 88 @max_content_length.setter89 def max_content_length(self, value: int | None) -> None:90 self._max_content_length = value91 92 @property93 def max_form_memory_size(self) -> int | None:94 """The maximum size in bytes any non-file form field may be in a95 ``multipart/form-data`` body. If this limit is exceeded, a 41396 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it97 is set to ``None``, no limit is enforced at the Flask application level.98 99 Each request defaults to the :data:`MAX_FORM_MEMORY_SIZE` config, which100 defaults to ``500_000``. It can be set on a specific ``request`` to101 apply the limit to that specific view. This should be set appropriately102 based on an application's or view's specific needs.103 104 .. versionchanged:: 3.1105 This is configurable through Flask config.106 """107 if self._max_form_memory_size is not None:108 return self._max_form_memory_size109 110 if not current_app:111 return super().max_form_memory_size112 113 return current_app.config["MAX_FORM_MEMORY_SIZE"] # type: ignore[no-any-return]114 115 @max_form_memory_size.setter116 def max_form_memory_size(self, value: int | None) -> None:117 self._max_form_memory_size = value118 119 @property # type: ignore[override]120 def max_form_parts(self) -> int | None:121 """The maximum number of fields that may be present in a122 ``multipart/form-data`` body. If this limit is exceeded, a 413123 :exc:`~werkzeug.exceptions.RequestEntityTooLarge` error is raised. If it124 is set to ``None``, no limit is enforced at the Flask application level.125 126 Each request defaults to the :data:`MAX_FORM_PARTS` config, which127 defaults to ``1_000``. It can be set on a specific ``request`` to apply128 the limit to that specific view. This should be set appropriately based129 on an application's or view's specific needs.130 131 .. versionchanged:: 3.1132 This is configurable through Flask config.133 """134 if self._max_form_parts is not None:135 return self._max_form_parts136 137 if not current_app:138 return super().max_form_parts139 140 return current_app.config["MAX_FORM_PARTS"] # type: ignore[no-any-return]141 142 @max_form_parts.setter143 def max_form_parts(self, value: int | None) -> None:144 self._max_form_parts = value145 146 @property147 def endpoint(self) -> str | None:148 """The endpoint that matched the request URL.149 150 This will be ``None`` if matching failed or has not been151 performed yet.152 153 This in combination with :attr:`view_args` can be used to154 reconstruct the same URL or a modified URL.155 """156 if self.url_rule is not None:157 return self.url_rule.endpoint # type: ignore[no-any-return]158 159 return None160 161 @property162 def blueprint(self) -> str | None:163 """The registered name of the current blueprint.164 165 This will be ``None`` if the endpoint is not part of a166 blueprint, or if URL matching failed or has not been performed167 yet.168 169 This does not necessarily match the name the blueprint was170 created with. It may have been nested, or registered with a171 different name.172 """173 endpoint = self.endpoint174 175 if endpoint is not None and "." in endpoint:176 return endpoint.rpartition(".")[0]177 178 return None179 180 @property181 def blueprints(self) -> list[str]:182 """The registered names of the current blueprint upwards through183 parent blueprints.184 185 This will be an empty list if there is no current blueprint, or186 if URL matching failed.187 188 .. versionadded:: 2.0.1189 """190 name = self.blueprint191 192 if name is None:193 return []194 195 return _split_blueprint_path(name)196 197 def _load_form_data(self) -> None:198 super()._load_form_data()199 200 # In debug mode we're replacing the files multidict with an ad-hoc201 # subclass that raises a different error for key errors.202 if (203 current_app204 and current_app.debug205 and self.mimetype != "multipart/form-data"206 and not self.files207 ):208 from .debughelpers import attach_enctype_error_multidict209 210 attach_enctype_error_multidict(self)211 212 def on_json_loading_failed(self, e: ValueError | None) -> t.Any:213 try:214 return super().on_json_loading_failed(e)215 except BadRequest as ebr:216 if current_app and current_app.debug:217 raise218 219 raise BadRequest() from ebr220 221 222class Response(ResponseBase):223 """The response object that is used by default in Flask. Works like the224 response object from Werkzeug but is set to have an HTML mimetype by225 default. Quite often you don't have to create this object yourself because226 :meth:`~flask.Flask.make_response` will take care of that for you.227 228 If you want to replace the response object used you can subclass this and229 set :attr:`~flask.Flask.response_class` to your subclass.230 231 .. versionchanged:: 1.0232 JSON support is added to the response, like the request. This is useful233 when testing to get the test client response data as JSON.234 235 .. versionchanged:: 1.0236 237 Added :attr:`max_cookie_size`.238 """239 240 default_mimetype: str | None = "text/html"241 242 json_module = json243 244 autocorrect_location_header = False245 246 @property247 def max_cookie_size(self) -> int: # type: ignore248 """Read-only view of the :data:`MAX_COOKIE_SIZE` config key.249 250 See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in251 Werkzeug's docs.252 """253 if current_app:254 return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return]255 256 # return Werkzeug's default when not in an app context257 return super().max_cookie_size258 