From fdba0d2526347786c05c15027e183a5ba4d81114 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 29 Jan 2021 10:35:37 -0800 Subject: [PATCH 1/7] remove JSONMixin --- docs/api.rst | 23 ++++++----------------- src/flask/wrappers.py | 26 +++++++++++--------------- 2 files changed, 17 insertions(+), 32 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index e5f68b8d..8ca1e42c 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -27,9 +27,9 @@ Incoming Request Data --------------------- .. autoclass:: Request - :members: - :inherited-members: - :exclude-members: json_module + :members: + :inherited-members: + :exclude-members: json_module .. attribute:: request @@ -48,20 +48,9 @@ Response Objects ---------------- .. autoclass:: flask.Response - :members: set_cookie, max_cookie_size, data, mimetype, is_json, get_json - - .. attribute:: headers - - 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. - + :members: + :inherited-members: + :exclude-members: json_module Sessions -------- diff --git a/src/flask/wrappers.py b/src/flask/wrappers.py index 76a12bd0..279561a6 100644 --- a/src/flask/wrappers.py +++ b/src/flask/wrappers.py @@ -1,23 +1,12 @@ from werkzeug.exceptions import BadRequest from werkzeug.wrappers import Request as RequestBase from werkzeug.wrappers import Response as ResponseBase -from werkzeug.wrappers.json import JSONMixin as _JSONMixin from . import json from .globals import current_app -class JSONMixin(_JSONMixin): - 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): +class Request(RequestBase): """The request object used by default in Flask. Remembers the matched endpoint and view arguments. @@ -30,6 +19,8 @@ class Request(RequestBase, JSONMixin): specific ones. """ + json_module = 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. @@ -89,8 +80,14 @@ class Request(RequestBase, JSONMixin): 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 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 @@ -110,8 +107,7 @@ class Response(JSONMixin, ResponseBase): default_mimetype = "text/html" - def _get_data_for_json(self, cache): - return self.get_data() + json_module = json @property def max_cookie_size(self): From 1936ca8a2e1789efd6169a901a8b60ec62ceb578 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 29 Jan 2021 10:38:40 -0800 Subject: [PATCH 2/7] remove BaseRequest and BaseResponse --- docs/testing.rst | 2 +- docs/tutorial/tests.rst | 2 +- src/flask/app.py | 2 +- src/flask/testing.py | 2 +- src/flask/wrappers.py | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 942a4e4d..2fedc600 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -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 application with the given path. The return value will be a :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. In this case, we ensure that ``'No entries here so far'`` is part of the output. diff --git a/docs/tutorial/tests.rst b/docs/tutorial/tests.rst index 079aeaa1..d8f2a931 100644 --- a/docs/tutorial/tests.rst +++ b/docs/tutorial/tests.rst @@ -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 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, -use :meth:`get_data(as_text=True) ` +use :meth:`get_data(as_text=True) ` instead. ``pytest.mark.parametrize`` tells Pytest to run the same test function diff --git a/src/flask/app.py b/src/flask/app.py index 34ca3700..4cbad04b 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -16,7 +16,7 @@ from werkzeug.routing import Map from werkzeug.routing import RequestRedirect from werkzeug.routing import RoutingException from werkzeug.routing import Rule -from werkzeug.wrappers import BaseResponse +from werkzeug.wrappers import Response as BaseResponse from . import cli from . import json diff --git a/src/flask/testing.py b/src/flask/testing.py index a316bf42..247e6605 100644 --- a/src/flask/testing.py +++ b/src/flask/testing.py @@ -5,7 +5,7 @@ import werkzeug.test from click.testing import CliRunner from werkzeug.test import Client from werkzeug.urls import url_parse -from werkzeug.wrappers import BaseRequest +from werkzeug.wrappers import Request as BaseRequest from . import _request_ctx_stack from .cli import ScriptInfo diff --git a/src/flask/wrappers.py b/src/flask/wrappers.py index 279561a6..1d8f17d7 100644 --- a/src/flask/wrappers.py +++ b/src/flask/wrappers.py @@ -113,7 +113,7 @@ class Response(ResponseBase): def max_cookie_size(self): """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. """ if current_app: From 14f56363a4992506f2b3670985fca4f0a29e5176 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 29 Jan 2021 10:44:46 -0800 Subject: [PATCH 3/7] rename send_file add_etags to etag --- CHANGES.rst | 3 ++- src/flask/helpers.py | 24 +++++++++++++++++++++--- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 8111d3ef..78951681 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -49,7 +49,8 @@ Unreleased implementations in ``werkzeug.utils``. :pr:`3828` - Some ``send_file`` parameters have been renamed, the old names are 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 ``as_attachment=False`` by using ``Content-Disposition: inline``. :pr:`3828` diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 325ac981..85277407 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -439,6 +439,8 @@ def get_flashed_messages(with_categories=False, category_filter=()): def _prepare_send_file_kwargs( download_name=None, attachment_filename=None, + etag=None, + add_etags=None, max_age=None, cache_timeout=None, **kwargs, @@ -461,12 +463,22 @@ def _prepare_send_file_kwargs( ) 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: max_age = current_app.get_send_file_max_age kwargs.update( environ=request.environ, download_name=download_name, + etag=etag, max_age=max_age, use_x_sendfile=current_app.use_x_sendfile, response_class=current_app.response_class, @@ -482,7 +494,8 @@ def send_file( download_name=None, attachment_filename=None, conditional=True, - add_etags=True, + etag=True, + add_etags=None, last_modified=None, max_age=None, cache_timeout=None, @@ -518,8 +531,8 @@ def send_file( the file. Defaults to the passed file name. :param conditional: Enable conditional and range responses based on request headers. Requires passing a file path and ``environ``. - :param add_etags: Calculate an ETag for the file. Requires passing a - file path. + :param etag: Calculate an ETag for the file, which requires passing + a file path. Can also be a string to use instead. :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 file path. @@ -537,6 +550,10 @@ def send_file( ``conditional`` is enabled and ``max_age`` is not set by 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 Passing a file-like object that inherits from :class:`~io.TextIOBase` will raise a :exc:`ValueError` rather @@ -593,6 +610,7 @@ def send_file( download_name=download_name, attachment_filename=attachment_filename, conditional=conditional, + etag=etag, add_etags=add_etags, last_modified=last_modified, max_age=max_age, From eb42655c46f8a598f2e0ead624ba5a65a8a66e90 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 29 Jan 2021 11:03:33 -0800 Subject: [PATCH 4/7] simplify BadRequestKeyError.show_exception --- src/flask/app.py | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/flask/app.py b/src/flask/app.py index 4cbad04b..d7d96cdb 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -1386,17 +1386,10 @@ class Flask(Scaffold): .. versionadded:: 0.7 """ - if isinstance(e, BadRequestKeyError): - if self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"]: - 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, BadRequestKeyError) and ( + self.debug or self.config["TRAP_BAD_REQUEST_ERRORS"] + ): + e.show_exception = True if isinstance(e, HTTPException) and not self.trap_http_exception(e): return self.handle_http_exception(e) From 64206c13c2bff0220328845db0995f451cefd3b1 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 29 Jan 2021 11:26:17 -0800 Subject: [PATCH 5/7] simplify InternalServerError.original_exception --- docs/errorhandling.rst | 22 ++++------------------ src/flask/app.py | 5 +---- 2 files changed, 5 insertions(+), 22 deletions(-) diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index d1b35699..764dfd20 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -249,25 +249,11 @@ be passed an instance of ``InternalServerError``, not the original unhandled error. 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 - - @app.errorhandler(InternalServerError) - def handle_500(e): - 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. +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 diff --git a/src/flask/app.py b/src/flask/app.py index d7d96cdb..e3ca8518 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -1447,10 +1447,7 @@ class Flask(Scaffold): raise e self.log_exception(exc_info) - server_error = InternalServerError() - # 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 + server_error = InternalServerError(original_exception=e) handler = self._find_error_handler(server_error) if handler is not None: From 055cdc2625c0cdf8cea87524f5c6bcf07f7e5005 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 29 Jan 2021 11:29:36 -0800 Subject: [PATCH 6/7] click detects program name when run as module --- src/flask/__main__.py | 2 +- src/flask/cli.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flask/__main__.py b/src/flask/__main__.py index 33b6c380..4e28416e 100644 --- a/src/flask/__main__.py +++ b/src/flask/__main__.py @@ -1,3 +1,3 @@ from .cli import main -main(as_module=True) +main() diff --git a/src/flask/cli.py b/src/flask/cli.py index 713d2b59..48c73763 100644 --- a/src/flask/cli.py +++ b/src/flask/cli.py @@ -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 - 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__": - main(as_module=True) + main() From 81ba6c24efea58dd00c093448fa2e529c0cda507 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 1 Feb 2021 19:14:58 -0800 Subject: [PATCH 7/7] remove Jinja 'autoescape' and 'with' extensions --- src/flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flask/app.py b/src/flask/app.py index e3ca8518..e58ee983 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -278,7 +278,7 @@ class Flask(Scaffold): #: This is a ``dict`` instead of an ``ImmutableDict`` to allow #: easier configuration. #: - jinja_options = {"extensions": ["jinja2.ext.autoescape", "jinja2.ext.with_"]} + jinja_options = {} #: Default configuration parameters. default_config = ImmutableDict(