Merge pull request #4576 from dzcode/dzcode/4567/abort-implementation
Add 'aborter' and 'aborter_class' attrs to 'Flask' object
This commit is contained in:
commit
5512a66881
5 changed files with 92 additions and 1 deletions
|
|
@ -5,6 +5,10 @@ Version 2.2.0
|
|||
|
||||
Unreleased
|
||||
|
||||
- Add ``aborter_class`` and ``aborter`` attributes to the Flask app
|
||||
object. ``flask.abort`` will call ``app.aborter``. This makes it
|
||||
possible for an app to override how aborts work, including custom
|
||||
status codes. :issue:`4567`
|
||||
- Add an ``app.redirect`` method, which ``flask.redirect`` will call.
|
||||
This makes it possible for an app to override how redirects work.
|
||||
:issue:`4569`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
from markupsafe import escape
|
||||
from markupsafe import Markup
|
||||
from werkzeug.exceptions import abort as abort
|
||||
|
||||
from . import json as json
|
||||
from .app import Flask as Flask
|
||||
|
|
@ -18,6 +17,7 @@ from .globals import current_app as current_app
|
|||
from .globals import g as g
|
||||
from .globals import request as request
|
||||
from .globals import session as session
|
||||
from .helpers import abort as abort
|
||||
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
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ from types import TracebackType
|
|||
|
||||
from werkzeug.datastructures import Headers
|
||||
from werkzeug.datastructures import ImmutableDict
|
||||
from werkzeug.exceptions import Aborter
|
||||
from werkzeug.exceptions import BadRequest
|
||||
from werkzeug.exceptions import BadRequestKeyError
|
||||
from werkzeug.exceptions import HTTPException
|
||||
|
|
@ -201,6 +202,16 @@ class Flask(Scaffold):
|
|||
#: :class:`~flask.Response` for more information.
|
||||
response_class = Response
|
||||
|
||||
#: The class of the object assigned to :attr:`aborter`, created by
|
||||
#: :meth:`create_aborter`. That object is called by
|
||||
#: :func:`flask.abort` to raise HTTP errors, and can be
|
||||
#: called directly as well.
|
||||
#:
|
||||
#: Defaults to :class:`werkzeug.exceptions.Aborter`.
|
||||
#:
|
||||
#: .. versionadded:: 2.2
|
||||
aborter_class = Aborter
|
||||
|
||||
#: The class that is used for the Jinja environment.
|
||||
#:
|
||||
#: .. versionadded:: 0.11
|
||||
|
|
@ -421,6 +432,13 @@ class Flask(Scaffold):
|
|||
#: to load a config from files.
|
||||
self.config = self.make_config(instance_relative_config)
|
||||
|
||||
#: An instance of :attr:`aborter_class` created by
|
||||
#: :meth:`make_aborter`. This is called by :func:`flask.abort`
|
||||
#: to raise HTTP errors, and can be called directly as well.
|
||||
#:
|
||||
#: .. versionadded:: 2.2
|
||||
self.aborter = self.make_aborter()
|
||||
|
||||
#: A list of functions that are called when :meth:`url_for` raises a
|
||||
#: :exc:`~werkzeug.routing.BuildError`. Each function registered here
|
||||
#: is called with `error`, `endpoint` and `values`. If a function
|
||||
|
|
@ -628,6 +646,18 @@ class Flask(Scaffold):
|
|||
defaults["DEBUG"] = get_debug_flag()
|
||||
return self.config_class(root_path, defaults)
|
||||
|
||||
def make_aborter(self) -> Aborter:
|
||||
"""Create the object to assign to :attr:`aborter`. That object
|
||||
is called by :func:`flask.abort` to raise HTTP errors, and can
|
||||
be called directly as well.
|
||||
|
||||
By default, this creates an instance of :attr:`aborter_class`,
|
||||
which defaults to :class:`werkzeug.exceptions.Aborter`.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
"""
|
||||
return self.aborter_class()
|
||||
|
||||
def auto_find_instance_path(self) -> str:
|
||||
"""Tries to locate the instance path if it was not provided to the
|
||||
constructor of the application class. It will basically calculate
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ from functools import update_wrapper
|
|||
from threading import RLock
|
||||
|
||||
import werkzeug.utils
|
||||
from werkzeug.exceptions import abort as _wz_abort
|
||||
from werkzeug.routing import BuildError
|
||||
from werkzeug.urls import url_quote
|
||||
from werkzeug.utils import redirect as _wz_redirect
|
||||
|
|
@ -24,6 +25,7 @@ from .signals import message_flashed
|
|||
if t.TYPE_CHECKING: # pragma: no cover
|
||||
from werkzeug.wrappers import Response as BaseResponse
|
||||
from .wrappers import Response
|
||||
import typing_extensions as te
|
||||
|
||||
|
||||
def get_env() -> str:
|
||||
|
|
@ -364,6 +366,31 @@ def redirect(
|
|||
return _wz_redirect(location, code=code, Response=Response)
|
||||
|
||||
|
||||
def abort( # type: ignore[misc]
|
||||
code: t.Union[int, "BaseResponse"], *args: t.Any, **kwargs: t.Any
|
||||
) -> "te.NoReturn":
|
||||
"""Raise an :exc:`~werkzeug.exceptions.HTTPException` for the given
|
||||
status code.
|
||||
|
||||
If :data:`~flask.current_app` is available, it will call its
|
||||
:attr:`~flask.Flask.aborter` object, otherwise it will use
|
||||
:func:`werkzeug.exceptions.abort`.
|
||||
|
||||
:param code: The status code for the exception, which must be
|
||||
registered in ``app.aborter``.
|
||||
:param args: Passed to the exception.
|
||||
:param kwargs: Passed to the exception.
|
||||
|
||||
.. versionadded:: 2.2
|
||||
Calls ``current_app.aborter`` if available instead of always
|
||||
using Werkzeug's default ``abort``.
|
||||
"""
|
||||
if current_app:
|
||||
current_app.aborter(code, *args, **kwargs)
|
||||
|
||||
_wz_abort(code, *args, **kwargs)
|
||||
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import io
|
|||
import os
|
||||
|
||||
import pytest
|
||||
import werkzeug.exceptions
|
||||
|
||||
import flask
|
||||
from flask.helpers import get_debug_flag
|
||||
|
|
@ -174,6 +175,35 @@ def test_redirect_with_app(app):
|
|||
flask.redirect("other")
|
||||
|
||||
|
||||
def test_abort_no_app():
|
||||
with pytest.raises(werkzeug.exceptions.Unauthorized):
|
||||
flask.abort(401)
|
||||
|
||||
with pytest.raises(LookupError):
|
||||
flask.abort(900)
|
||||
|
||||
|
||||
def test_app_aborter_class():
|
||||
class MyAborter(werkzeug.exceptions.Aborter):
|
||||
pass
|
||||
|
||||
class MyFlask(flask.Flask):
|
||||
aborter_class = MyAborter
|
||||
|
||||
app = MyFlask(__name__)
|
||||
assert isinstance(app.aborter, MyAborter)
|
||||
|
||||
|
||||
def test_abort_with_app(app):
|
||||
class My900Error(werkzeug.exceptions.HTTPException):
|
||||
code = 900
|
||||
|
||||
app.aborter.mapping[900] = My900Error
|
||||
|
||||
with app.app_context(), pytest.raises(My900Error):
|
||||
flask.abort(900)
|
||||
|
||||
|
||||
class TestNoImports:
|
||||
"""Test Flasks are created without import.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue