Merge pull request #3894 from pallets/update-werkzeug
Update for recent changes in Pallets libraries
This commit is contained in:
commit
9f7ac04aaf
11 changed files with 59 additions and 79 deletions
|
|
@ -49,7 +49,8 @@ Unreleased
|
||||||
implementations in ``werkzeug.utils``. :pr:`3828`
|
implementations in ``werkzeug.utils``. :pr:`3828`
|
||||||
- Some ``send_file`` parameters have been renamed, the old names are
|
- Some ``send_file`` parameters have been renamed, the old names are
|
||||||
deprecated. ``attachment_filename`` is renamed to ``download_name``.
|
deprecated. ``attachment_filename`` is renamed to ``download_name``.
|
||||||
``cache_timeout`` is renamed to ``max_age``. :pr:`3828`
|
``cache_timeout`` is renamed to ``max_age``. ``add_etags`` is
|
||||||
|
renamed to ``etag``. :pr:`3828, 3883`
|
||||||
- ``send_file`` passes ``download_name`` even if
|
- ``send_file`` passes ``download_name`` even if
|
||||||
``as_attachment=False`` by using ``Content-Disposition: inline``.
|
``as_attachment=False`` by using ``Content-Disposition: inline``.
|
||||||
:pr:`3828`
|
:pr:`3828`
|
||||||
|
|
|
||||||
23
docs/api.rst
23
docs/api.rst
|
|
@ -27,9 +27,9 @@ Incoming Request Data
|
||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
.. autoclass:: Request
|
.. autoclass:: Request
|
||||||
:members:
|
:members:
|
||||||
:inherited-members:
|
:inherited-members:
|
||||||
:exclude-members: json_module
|
:exclude-members: json_module
|
||||||
|
|
||||||
.. attribute:: request
|
.. attribute:: request
|
||||||
|
|
||||||
|
|
@ -48,20 +48,9 @@ Response Objects
|
||||||
----------------
|
----------------
|
||||||
|
|
||||||
.. autoclass:: flask.Response
|
.. autoclass:: flask.Response
|
||||||
:members: set_cookie, max_cookie_size, data, mimetype, is_json, get_json
|
:members:
|
||||||
|
:inherited-members:
|
||||||
.. attribute:: headers
|
:exclude-members: json_module
|
||||||
|
|
||||||
A :class:`~werkzeug.datastructures.Headers` object representing the response headers.
|
|
||||||
|
|
||||||
.. attribute:: status
|
|
||||||
|
|
||||||
A string with a response status.
|
|
||||||
|
|
||||||
.. attribute:: status_code
|
|
||||||
|
|
||||||
The response status as integer.
|
|
||||||
|
|
||||||
|
|
||||||
Sessions
|
Sessions
|
||||||
--------
|
--------
|
||||||
|
|
|
||||||
|
|
@ -249,25 +249,11 @@ be passed an instance of ``InternalServerError``, not the original
|
||||||
unhandled error.
|
unhandled error.
|
||||||
|
|
||||||
The original error is available as ``e.original_exception``.
|
The original error is available as ``e.original_exception``.
|
||||||
Until Werkzeug 1.0.0, this attribute will only exist during unhandled
|
|
||||||
errors, use ``getattr`` to get access it for compatibility.
|
|
||||||
|
|
||||||
.. code-block:: python
|
An error handler for "500 Internal Server Error" will be passed uncaught
|
||||||
|
exceptions in addition to explicit 500 errors. In debug mode, a handler
|
||||||
@app.errorhandler(InternalServerError)
|
for "500 Internal Server Error" will not be used. Instead, the
|
||||||
def handle_500(e):
|
interactive debugger will be shown.
|
||||||
original = getattr(e, "original_exception", None)
|
|
||||||
|
|
||||||
if original is None:
|
|
||||||
# direct 500 error, such as abort(500)
|
|
||||||
return render_template("500.html"), 500
|
|
||||||
|
|
||||||
# wrapped unhandled error
|
|
||||||
return render_template("500_unhandled.html", e=original), 500
|
|
||||||
|
|
||||||
An error handler for "500 Internal Server Error" will be passed uncaught exceptions in
|
|
||||||
addition to explicit 500 errors. In debug mode, a handler for "500 Internal Server Error" will not be used.
|
|
||||||
Instead, the interactive debugger will be shown.
|
|
||||||
|
|
||||||
|
|
||||||
Custom Error Pages
|
Custom Error Pages
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ Notice that our test functions begin with the word `test`; this allows
|
||||||
By using ``client.get`` we can send an HTTP ``GET`` request to the
|
By using ``client.get`` we can send an HTTP ``GET`` request to the
|
||||||
application with the given path. The return value will be a
|
application with the given path. The return value will be a
|
||||||
:class:`~flask.Flask.response_class` object. We can now use the
|
:class:`~flask.Flask.response_class` object. We can now use the
|
||||||
:attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect
|
:attr:`~werkzeug.wrappers.Response.data` attribute to inspect
|
||||||
the return value (as string) from the application.
|
the return value (as string) from the application.
|
||||||
In this case, we ensure that ``'No entries here so far'``
|
In this case, we ensure that ``'No entries here so far'``
|
||||||
is part of the output.
|
is part of the output.
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ URL when the register view redirects to the login view.
|
||||||
:attr:`~Response.data` contains the body of the response as bytes. If
|
:attr:`~Response.data` contains the body of the response as bytes. If
|
||||||
you expect a certain value to render on the page, check that it's in
|
you expect a certain value to render on the page, check that it's in
|
||||||
``data``. Bytes must be compared to bytes. If you want to compare text,
|
``data``. Bytes must be compared to bytes. If you want to compare text,
|
||||||
use :meth:`get_data(as_text=True) <werkzeug.wrappers.BaseResponse.get_data>`
|
use :meth:`get_data(as_text=True) <werkzeug.wrappers.Response.get_data>`
|
||||||
instead.
|
instead.
|
||||||
|
|
||||||
``pytest.mark.parametrize`` tells Pytest to run the same test function
|
``pytest.mark.parametrize`` tells Pytest to run the same test function
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
from .cli import main
|
from .cli import main
|
||||||
|
|
||||||
main(as_module=True)
|
main()
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ from werkzeug.routing import Map
|
||||||
from werkzeug.routing import RequestRedirect
|
from werkzeug.routing import RequestRedirect
|
||||||
from werkzeug.routing import RoutingException
|
from werkzeug.routing import RoutingException
|
||||||
from werkzeug.routing import Rule
|
from werkzeug.routing import Rule
|
||||||
from werkzeug.wrappers import BaseResponse
|
from werkzeug.wrappers import Response as BaseResponse
|
||||||
|
|
||||||
from . import cli
|
from . import cli
|
||||||
from . import json
|
from . import json
|
||||||
|
|
@ -278,7 +278,7 @@ class Flask(Scaffold):
|
||||||
#: This is a ``dict`` instead of an ``ImmutableDict`` to allow
|
#: This is a ``dict`` instead of an ``ImmutableDict`` to allow
|
||||||
#: easier configuration.
|
#: easier configuration.
|
||||||
#:
|
#:
|
||||||
jinja_options = {"extensions": ["jinja2.ext.autoescape", "jinja2.ext.with_"]}
|
jinja_options = {}
|
||||||
|
|
||||||
#: Default configuration parameters.
|
#: Default configuration parameters.
|
||||||
default_config = ImmutableDict(
|
default_config = ImmutableDict(
|
||||||
|
|
@ -1386,17 +1386,10 @@ class Flask(Scaffold):
|
||||||
|
|
||||||
.. versionadded:: 0.7
|
.. versionadded:: 0.7
|
||||||
"""
|
"""
|
||||||
if isinstance(e, BadRequestKeyError):
|
if isinstance(e, BadRequestKeyError) and (
|
||||||
if self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]:
|
self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]
|
||||||
e.show_exception = True
|
):
|
||||||
|
e.show_exception = True
|
||||||
# Werkzeug < 0.15 doesn't add the KeyError to the 400
|
|
||||||
# message, add it in manually.
|
|
||||||
# TODO: clean up once Werkzeug >= 0.15.5 is required
|
|
||||||
if e.args[0] not in e.get_description():
|
|
||||||
e.description = f"KeyError: {e.args[0]!r}"
|
|
||||||
elif not hasattr(BadRequestKeyError, "show_exception"):
|
|
||||||
e.args = ()
|
|
||||||
|
|
||||||
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
|
if isinstance(e, HTTPException) and not self.trap_http_exception(e):
|
||||||
return self.handle_http_exception(e)
|
return self.handle_http_exception(e)
|
||||||
|
|
@ -1454,10 +1447,7 @@ class Flask(Scaffold):
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
self.log_exception(exc_info)
|
self.log_exception(exc_info)
|
||||||
server_error = InternalServerError()
|
server_error = InternalServerError(original_exception=e)
|
||||||
# TODO: pass as param when Werkzeug>=1.0.0 is required
|
|
||||||
# TODO: also remove note about this from docstring and docs
|
|
||||||
server_error.original_exception = e
|
|
||||||
handler = self._find_error_handler(server_error)
|
handler = self._find_error_handler(server_error)
|
||||||
|
|
||||||
if handler is not None:
|
if handler is not None:
|
||||||
|
|
|
||||||
|
|
@ -953,10 +953,10 @@ debug mode.
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def main(as_module=False):
|
def main():
|
||||||
# TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed
|
# TODO omit sys.argv once https://github.com/pallets/click/issues/536 is fixed
|
||||||
cli.main(args=sys.argv[1:], prog_name="python -m flask" if as_module else None)
|
cli.main(args=sys.argv[1:])
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
main(as_module=True)
|
main()
|
||||||
|
|
|
||||||
|
|
@ -439,6 +439,8 @@ def get_flashed_messages(with_categories=False, category_filter=()):
|
||||||
def _prepare_send_file_kwargs(
|
def _prepare_send_file_kwargs(
|
||||||
download_name=None,
|
download_name=None,
|
||||||
attachment_filename=None,
|
attachment_filename=None,
|
||||||
|
etag=None,
|
||||||
|
add_etags=None,
|
||||||
max_age=None,
|
max_age=None,
|
||||||
cache_timeout=None,
|
cache_timeout=None,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
|
|
@ -461,12 +463,22 @@ def _prepare_send_file_kwargs(
|
||||||
)
|
)
|
||||||
max_age = cache_timeout
|
max_age = cache_timeout
|
||||||
|
|
||||||
|
if add_etags is not None:
|
||||||
|
warnings.warn(
|
||||||
|
"The 'add_etags' parameter has been renamed to 'etag'. The old name will be"
|
||||||
|
" removed in Flask 2.1.",
|
||||||
|
DeprecationWarning,
|
||||||
|
stacklevel=3,
|
||||||
|
)
|
||||||
|
etag = add_etags
|
||||||
|
|
||||||
if max_age is None:
|
if max_age is None:
|
||||||
max_age = current_app.get_send_file_max_age
|
max_age = current_app.get_send_file_max_age
|
||||||
|
|
||||||
kwargs.update(
|
kwargs.update(
|
||||||
environ=request.environ,
|
environ=request.environ,
|
||||||
download_name=download_name,
|
download_name=download_name,
|
||||||
|
etag=etag,
|
||||||
max_age=max_age,
|
max_age=max_age,
|
||||||
use_x_sendfile=current_app.use_x_sendfile,
|
use_x_sendfile=current_app.use_x_sendfile,
|
||||||
response_class=current_app.response_class,
|
response_class=current_app.response_class,
|
||||||
|
|
@ -482,7 +494,8 @@ def send_file(
|
||||||
download_name=None,
|
download_name=None,
|
||||||
attachment_filename=None,
|
attachment_filename=None,
|
||||||
conditional=True,
|
conditional=True,
|
||||||
add_etags=True,
|
etag=True,
|
||||||
|
add_etags=None,
|
||||||
last_modified=None,
|
last_modified=None,
|
||||||
max_age=None,
|
max_age=None,
|
||||||
cache_timeout=None,
|
cache_timeout=None,
|
||||||
|
|
@ -518,8 +531,8 @@ def send_file(
|
||||||
the file. Defaults to the passed file name.
|
the file. Defaults to the passed file name.
|
||||||
:param conditional: Enable conditional and range responses based on
|
:param conditional: Enable conditional and range responses based on
|
||||||
request headers. Requires passing a file path and ``environ``.
|
request headers. Requires passing a file path and ``environ``.
|
||||||
:param add_etags: Calculate an ETag for the file. Requires passing a
|
:param etag: Calculate an ETag for the file, which requires passing
|
||||||
file path.
|
a file path. Can also be a string to use instead.
|
||||||
:param last_modified: The last modified time to send for the file,
|
:param last_modified: The last modified time to send for the file,
|
||||||
in seconds. If not provided, it will try to detect it from the
|
in seconds. If not provided, it will try to detect it from the
|
||||||
file path.
|
file path.
|
||||||
|
|
@ -537,6 +550,10 @@ def send_file(
|
||||||
``conditional`` is enabled and ``max_age`` is not set by
|
``conditional`` is enabled and ``max_age`` is not set by
|
||||||
default.
|
default.
|
||||||
|
|
||||||
|
.. versionchanged:: 2.0.0
|
||||||
|
``etag`` replaces the ``add_etags`` parameter. It can be a
|
||||||
|
string to use instead of generating one.
|
||||||
|
|
||||||
.. versionchanged:: 2.0
|
.. versionchanged:: 2.0
|
||||||
Passing a file-like object that inherits from
|
Passing a file-like object that inherits from
|
||||||
:class:`~io.TextIOBase` will raise a :exc:`ValueError` rather
|
:class:`~io.TextIOBase` will raise a :exc:`ValueError` rather
|
||||||
|
|
@ -593,6 +610,7 @@ def send_file(
|
||||||
download_name=download_name,
|
download_name=download_name,
|
||||||
attachment_filename=attachment_filename,
|
attachment_filename=attachment_filename,
|
||||||
conditional=conditional,
|
conditional=conditional,
|
||||||
|
etag=etag,
|
||||||
add_etags=add_etags,
|
add_etags=add_etags,
|
||||||
last_modified=last_modified,
|
last_modified=last_modified,
|
||||||
max_age=max_age,
|
max_age=max_age,
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ import werkzeug.test
|
||||||
from click.testing import CliRunner
|
from click.testing import CliRunner
|
||||||
from werkzeug.test import Client
|
from werkzeug.test import Client
|
||||||
from werkzeug.urls import url_parse
|
from werkzeug.urls import url_parse
|
||||||
from werkzeug.wrappers import BaseRequest
|
from werkzeug.wrappers import Request as BaseRequest
|
||||||
|
|
||||||
from . import _request_ctx_stack
|
from . import _request_ctx_stack
|
||||||
from .cli import ScriptInfo
|
from .cli import ScriptInfo
|
||||||
|
|
|
||||||
|
|
@ -1,23 +1,12 @@
|
||||||
from werkzeug.exceptions import BadRequest
|
from werkzeug.exceptions import BadRequest
|
||||||
from werkzeug.wrappers import Request as RequestBase
|
from werkzeug.wrappers import Request as RequestBase
|
||||||
from werkzeug.wrappers import Response as ResponseBase
|
from werkzeug.wrappers import Response as ResponseBase
|
||||||
from werkzeug.wrappers.json import JSONMixin as _JSONMixin
|
|
||||||
|
|
||||||
from . import json
|
from . import json
|
||||||
from .globals import current_app
|
from .globals import current_app
|
||||||
|
|
||||||
|
|
||||||
class JSONMixin(_JSONMixin):
|
class Request(RequestBase):
|
||||||
json_module = json
|
|
||||||
|
|
||||||
def on_json_loading_failed(self, e):
|
|
||||||
if current_app and current_app.debug:
|
|
||||||
raise BadRequest(f"Failed to decode JSON object: {e}")
|
|
||||||
|
|
||||||
raise BadRequest()
|
|
||||||
|
|
||||||
|
|
||||||
class Request(RequestBase, JSONMixin):
|
|
||||||
"""The request object used by default in Flask. Remembers the
|
"""The request object used by default in Flask. Remembers the
|
||||||
matched endpoint and view arguments.
|
matched endpoint and view arguments.
|
||||||
|
|
||||||
|
|
@ -30,6 +19,8 @@ class Request(RequestBase, JSONMixin):
|
||||||
specific ones.
|
specific ones.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
json_module = json
|
||||||
|
|
||||||
#: The internal URL rule that matched the request. This can be
|
#: The internal URL rule that matched the request. This can be
|
||||||
#: useful to inspect which methods are allowed for the URL from
|
#: useful to inspect which methods are allowed for the URL from
|
||||||
#: a before/after handler (``request.url_rule.methods``) etc.
|
#: a before/after handler (``request.url_rule.methods``) etc.
|
||||||
|
|
@ -89,8 +80,14 @@ class Request(RequestBase, JSONMixin):
|
||||||
|
|
||||||
attach_enctype_error_multidict(self)
|
attach_enctype_error_multidict(self)
|
||||||
|
|
||||||
|
def on_json_loading_failed(self, e):
|
||||||
|
if current_app and current_app.debug:
|
||||||
|
raise BadRequest(f"Failed to decode JSON object: {e}")
|
||||||
|
|
||||||
class Response(JSONMixin, ResponseBase):
|
raise BadRequest()
|
||||||
|
|
||||||
|
|
||||||
|
class Response(ResponseBase):
|
||||||
"""The response object that is used by default in Flask. Works like the
|
"""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
|
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
|
default. Quite often you don't have to create this object yourself because
|
||||||
|
|
@ -110,14 +107,13 @@ class Response(JSONMixin, ResponseBase):
|
||||||
|
|
||||||
default_mimetype = "text/html"
|
default_mimetype = "text/html"
|
||||||
|
|
||||||
def _get_data_for_json(self, cache):
|
json_module = json
|
||||||
return self.get_data()
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def max_cookie_size(self):
|
def max_cookie_size(self):
|
||||||
"""Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
|
"""Read-only view of the :data:`MAX_COOKIE_SIZE` config key.
|
||||||
|
|
||||||
See :attr:`~werkzeug.wrappers.BaseResponse.max_cookie_size` in
|
See :attr:`~werkzeug.wrappers.Response.max_cookie_size` in
|
||||||
Werkzeug's docs.
|
Werkzeug's docs.
|
||||||
"""
|
"""
|
||||||
if current_app:
|
if current_app:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue