From fdab801fbbd9de5adbdb3320ca4a1cb116c892f5 Mon Sep 17 00:00:00 2001 From: Tim Hoagland Date: Mon, 2 May 2022 12:44:15 -0400 Subject: [PATCH] add redirect method to app --- CHANGES.rst | 3 +++ src/flask/__init__.py | 2 +- src/flask/app.py | 11 +++++++++++ src/flask/helpers.py | 24 ++++++++++++++++++++++++ tests/test_helpers.py | 16 ++++++++++++++++ 5 files changed, 55 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index 285d6326..71100355 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -5,6 +5,9 @@ Version 2.2.0 Unreleased +- Add an ``app.redirect`` method, which ``flask.redirect`` will call. + This makes it possible for an app to override how redirects work. + :issue:`4569` - Refactor ``register_error_handler`` to consolidate error checking. Rewrite some error messages to be more consistent. :issue:`4559` diff --git a/src/flask/__init__.py b/src/flask/__init__.py index f4b8c75c..f71a7d42 100644 --- a/src/flask/__init__.py +++ b/src/flask/__init__.py @@ -1,7 +1,6 @@ from markupsafe import escape from markupsafe import Markup from werkzeug.exceptions import abort as abort -from werkzeug.utils import redirect as redirect from . import json as json from .app import Flask as Flask @@ -23,6 +22,7 @@ from .helpers import flash as flash from .helpers import get_flashed_messages as get_flashed_messages from .helpers import get_template_attribute as get_template_attribute from .helpers import make_response as make_response +from .helpers import redirect as redirect from .helpers import send_file as send_file from .helpers import send_from_directory as send_from_directory from .helpers import stream_with_context as stream_with_context diff --git a/src/flask/app.py b/src/flask/app.py index ac82f72a..8ce14301 100644 --- a/src/flask/app.py +++ b/src/flask/app.py @@ -22,6 +22,7 @@ from werkzeug.routing import MapAdapter from werkzeug.routing import RequestRedirect from werkzeug.routing import RoutingException from werkzeug.routing import Rule +from werkzeug.utils import redirect as _wz_redirect from werkzeug.wrappers import Response as BaseResponse from . import cli @@ -1630,6 +1631,16 @@ class Flask(Scaffold): return asgiref_async_to_sync(func) + def redirect(self, location: str, code: int = 302) -> BaseResponse: + """Create a redirect response object. + + :param location: the url of the redirect + :param code: http return code + + .. versionadded:: 2.2 + """ + return _wz_redirect(location, code=code, Response=self.response_class) + def make_response(self, rv: ResponseReturnValue) -> Response: """Convert the return value from a view function to an instance of :attr:`response_class`. diff --git a/src/flask/helpers.py b/src/flask/helpers.py index 656dab76..e3883ed6 100644 --- a/src/flask/helpers.py +++ b/src/flask/helpers.py @@ -12,6 +12,7 @@ from threading import RLock import werkzeug.utils from werkzeug.routing import BuildError from werkzeug.urls import url_quote +from werkzeug.utils import redirect as _wz_redirect from .globals import _app_ctx_stack from .globals import _request_ctx_stack @@ -21,6 +22,7 @@ from .globals import session from .signals import message_flashed if t.TYPE_CHECKING: # pragma: no cover + from werkzeug.wrappers import Response as BaseResponse from .wrappers import Response @@ -340,6 +342,28 @@ def url_for(endpoint: str, **values: t.Any) -> str: return rv +def redirect( + location: str, code: int = 302, Response: t.Optional[t.Type["BaseResponse"]] = None +) -> "BaseResponse": + """Create a redirect response object. + + If :data:`~flask.current_app` is available, it will use + :meth:`~flask.app.Flask.redirect`, otherwise it will use + :func:`werkzeug.utils.redirect`. + + :param location: The URL to redirect to. + :param code: The status code for the redirect. + :param Response: The response class to use. Not used when + ``current_app`` is active, which uses ``app.response_class``. + + .. versionadded:: 2.2 + """ + if current_app: + return current_app.redirect(location, code=code) + + return _wz_redirect(location, code=code, Response=Response) + + def get_template_attribute(template_name: str, attribute: str) -> t.Any: """Loads a macro (or variable) a template exports. This can be used to invoke a macro from within Python code. If you for example have a diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 58bf3c0b..63dbbc13 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -158,6 +158,22 @@ class TestUrlFor: assert flask.url_for("myview", _method="POST") == "/myview/create" +def test_redirect_no_app(): + response = flask.redirect("https://localhost", 307) + assert response.location == "https://localhost" + assert response.status_code == 307 + + +def test_redirect_with_app(app): + def redirect(location, code=302): + raise ValueError + + app.redirect = redirect + + with app.app_context(), pytest.raises(ValueError): + flask.redirect("other") + + class TestNoImports: """Test Flasks are created without import.