wrappers.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. from __future__ import annotations
  2. import typing as t
  3. from werkzeug.exceptions import BadRequest
  4. from werkzeug.exceptions import HTTPException
  5. from werkzeug.wrappers import Request as RequestBase
  6. from werkzeug.wrappers import Response as ResponseBase
  7. from . import json
  8. from .globals import current_app
  9. from .helpers import _split_blueprint_path
  10. if t.TYPE_CHECKING: # pragma: no cover
  11. from werkzeug.routing import Rule
  12. class Request(RequestBase):
  13. """The request object used by default in Flask. Remembers the
  14. matched endpoint and view arguments.
  15. It is what ends up as :class:`~flask.request`. If you want to replace
  16. the request object used you can subclass this and set
  17. :attr:`~flask.Flask.request_class` to your subclass.
  18. The request object is a :class:`~werkzeug.wrappers.Request` subclass and
  19. provides all of the attributes Werkzeug defines plus a few Flask
  20. specific ones.
  21. """
  22. json_module: t.Any = json
  23. #: The internal URL rule that matched the request. This can be
  24. #: useful to inspect which methods are allowed for the URL from
  25. #: a before/after handler (``request.url_rule.methods``) etc.
  26. #: Though if the request's method was invalid for the URL rule,
  27. #: the valid list is available in ``routing_exception.valid_methods``
  28. #: instead (an attribute of the Werkzeug exception
  29. #: :exc:`~werkzeug.exceptions.MethodNotAllowed`)
  30. #: because the request was never internally bound.
  31. #:
  32. #: .. versionadded:: 0.6
  33. url_rule: Rule | None = None
  34. #: A dict of view arguments that matched the request. If an exception
  35. #: happened when matching, this will be ``None``.
  36. view_args: dict[str, t.Any] | None = None
  37. #: If matching the URL failed, this is the exception that will be
  38. #: raised / was raised as part of the request handling. This is
  39. #: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
  40. #: something similar.
  41. routing_exception: HTTPException | None = None
  42. @property
  43. def max_content_length(self) -> int | None: # type: ignore[override]
  44. """Read-only view of the ``MAX_CONTENT_LENGTH`` config key."""
  45. if current_app:
  46. return current_app.config["MAX_CONTENT_LENGTH"] # type: ignore[no-any-return]
  47. else:
  48. return None
  49. @property
  50. def endpoint(self) -> str | None:
  51. """The endpoint that matched the request URL.
  52. This will be ``None`` if matching failed or has not been
  53. performed yet.
  54. This in combination with :attr:`view_args` can be used to
  55. reconstruct the same URL or a modified URL.
  56. """
  57. if self.url_rule is not None:
  58. return self.url_rule.endpoint
  59. return None
  60. @property
  61. def blueprint(self) -> str | None:
  62. """The registered name of the current blueprint.
  63. This will be ``None`` if the endpoint is not part of a
  64. blueprint, or if URL matching failed or has not been performed
  65. yet.
  66. This does not necessarily match the name the blueprint was
  67. created with. It may have been nested, or registered with a
  68. different name.
  69. """
  70. endpoint = self.endpoint
  71. if endpoint is not None and "." in endpoint:
  72. return endpoint.rpartition(".")[0]
  73. return None
  74. @property
  75. def blueprints(self) -> list[str]:
  76. """The registered names of the current blueprint upwards through
  77. parent blueprints.
  78. This will be an empty list if there is no current blueprint, or
  79. if URL matching failed.
  80. .. versionadded:: 2.0.1
  81. """
  82. name = self.blueprint
  83. if name is None:
  84. return []
  85. return _split_blueprint_path(name)
  86. def _load_form_data(self) -> None:
  87. super()._load_form_data()
  88. # In debug mode we're replacing the files multidict with an ad-hoc
  89. # subclass that raises a different error for key errors.
  90. if (
  91. current_app
  92. and current_app.debug
  93. and self.mimetype != "multipart/form-data"
  94. and not self.files
  95. ):
  96. from .debughelpers import attach_enctype_error_multidict
  97. attach_enctype_error_multidict(self)
  98. def on_json_loading_failed(self, e: ValueError | None) -> t.Any:
  99. try:
  100. return super().on_json_loading_failed(e)
  101. except BadRequest as e:
  102. if current_app and current_app.debug:
  103. raise
  104. raise BadRequest() from e
  105. class Response(ResponseBase):
  106. """The response object that is used by default in Flask. Works like the
  107. response object from Werkzeug but is set to have an HTML mimetype by
  108. default. Quite often you don't have to create this object yourself because
  109. :meth:`~flask.Flask.make_response` will take care of that for you.
  110. If you want to replace the response object used you can subclass this and
  111. set :attr:`~flask.Flask.response_class` to your subclass.
  112. .. versionchanged:: 1.0
  113. JSON support is added to the response, like the request. This is useful
  114. when testing to get the test client response data as JSON.
  115. .. versionchanged:: 1.0
  116. Added :attr:`max_cookie_size`.
  117. """
  118. default_mimetype: str | None = "text/html"
  119. json_module = json
  120. autocorrect_location_header = False
  121. @property
  122. def max_cookie_size(self) -> int: # type: ignore
  123. """Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
  124. See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
  125. Werkzeug's docs.
  126. """
  127. if current_app:
  128. return current_app.config["MAX_COOKIE_SIZE"] # type: ignore[no-any-return]
  129. # return Werkzeug's default when not in an app context
  130. return super().max_cookie_size