Adds Blueprint Group exception decorator (#2238)

* Add exception decorator

* Added tests

* Fix line too long
This commit is contained in:
Néstor Pérez 2021-09-12 21:02:59 +02:00 committed by GitHub
parent a937e08ef0
commit 404c5f9f9e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 53 additions and 2 deletions

View File

@ -197,6 +197,27 @@ class BlueprintGroup(MutableSequence):
"""
self._blueprints.append(value)
def exception(self, *exceptions, **kwargs):
"""
A decorator that can be used to implement a global exception handler
for all the Blueprints that belong to this Blueprint Group.
In case of nested Blueprint Groups, the same handler is applied
across each of the Blueprints recursively.
:param args: List of Python exceptions to be caught by the handler
:param kwargs: Additional optional arguments to be passed to the
exception handler
:return a decorated method to handle global exceptions for any
blueprint registered under this group.
"""
def register_exception_handler_for_blueprints(fn):
for blueprint in self.blueprints:
blueprint.exception(*exceptions, **kwargs)(fn)
return register_exception_handler_for_blueprints
def insert(self, index: int, item: Blueprint) -> None:
"""
The Abstract class `MutableSequence` leverages this insert method to

View File

@ -3,6 +3,7 @@ from pytest import raises
from sanic.app import Sanic
from sanic.blueprint_group import BlueprintGroup
from sanic.blueprints import Blueprint
from sanic.exceptions import Forbidden, InvalidUsage, SanicException, ServerError
from sanic.request import Request
from sanic.response import HTTPResponse, text
@ -96,16 +97,28 @@ def test_bp_group(app: Sanic):
def blueprint_1_default_route(request):
return text("BP1_OK")
@blueprint_1.route("/invalid")
def blueprint_1_error(request: Request):
raise InvalidUsage("Invalid")
@blueprint_2.route("/")
def blueprint_2_default_route(request):
return text("BP2_OK")
@blueprint_2.route("/error")
def blueprint_2_error(request: Request):
raise ServerError("Error")
blueprint_group_1 = Blueprint.group(
blueprint_1, blueprint_2, url_prefix="/bp"
)
blueprint_3 = Blueprint("blueprint_3", url_prefix="/bp3")
@blueprint_group_1.exception(InvalidUsage)
def handle_group_exception(request, exception):
return text("BP1_ERR_OK")
@blueprint_group_1.middleware("request")
def blueprint_group_1_middleware(request):
global MIDDLEWARE_INVOKE_COUNTER
@ -130,10 +143,18 @@ def test_bp_group(app: Sanic):
def blueprint_3_default_route(request):
return text("BP3_OK")
@blueprint_3.route("/forbidden")
def blueprint_3_forbidden(request: Request):
raise Forbidden("Forbidden")
blueprint_group_2 = Blueprint.group(
blueprint_group_1, blueprint_3, url_prefix="/api"
)
@blueprint_group_2.exception(SanicException)
def handle_non_handled_exception(request, exception):
return text("BP2_ERR_OK")
@blueprint_group_2.middleware("response")
def blueprint_group_2_middleware(request, response):
global MIDDLEWARE_INVOKE_COUNTER
@ -161,14 +182,23 @@ def test_bp_group(app: Sanic):
_, response = app.test_client.get("/api/bp/bp1")
assert response.text == "BP1_OK"
_, response = app.test_client.get("/api/bp/bp1/invalid")
assert response.text == "BP1_ERR_OK"
_, response = app.test_client.get("/api/bp/bp2")
assert response.text == "BP2_OK"
_, response = app.test_client.get("/api/bp/bp2/error")
assert response.text == "BP2_ERR_OK"
_, response = app.test_client.get("/api/bp3")
assert response.text == "BP3_OK"
assert MIDDLEWARE_INVOKE_COUNTER["response"] == 9
assert MIDDLEWARE_INVOKE_COUNTER["request"] == 8
_, response = app.test_client.get("/api/bp3/forbidden")
assert response.text == "BP2_ERR_OK"
assert MIDDLEWARE_INVOKE_COUNTER["response"] == 18
assert MIDDLEWARE_INVOKE_COUNTER["request"] == 16
def test_bp_group_list_operations(app: Sanic):