From c21cf10059c8d5ee5f99b56f59757a44d8e508f6 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Mon, 2 Aug 2021 01:50:37 +0300 Subject: [PATCH 1/5] WIP on named routes per exception handling --- sanic/app.py | 10 +++++++--- sanic/blueprints.py | 4 +++- sanic/handlers.py | 47 ++++++++++++++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/sanic/app.py b/sanic/app.py index ac346d7381..30991ae305 100644 --- a/sanic/app.py +++ b/sanic/app.py @@ -310,7 +310,11 @@ def register_named_middleware( self.named_response_middleware[_rn].appendleft(middleware) return middleware - def _apply_exception_handler(self, handler: FutureException): + def _apply_exception_handler( + self, + handler: FutureException, + route_names: Optional[List[str]] = None, + ): """Decorate a function to be registered as a handler for exceptions :param exceptions: exceptions @@ -320,9 +324,9 @@ def _apply_exception_handler(self, handler: FutureException): for exception in handler.exceptions: if isinstance(exception, (tuple, list)): for e in exception: - self.error_handler.add(e, handler.handler) + self.error_handler.add(e, handler.handler, route_names) else: - self.error_handler.add(exception, handler.handler) + self.error_handler.add(exception, handler.handler, route_names) return handler.handler def _apply_listener(self, listener: FutureListener): diff --git a/sanic/blueprints.py b/sanic/blueprints.py index f0d7f431a0..baa7375401 100644 --- a/sanic/blueprints.py +++ b/sanic/blueprints.py @@ -268,7 +268,9 @@ def register(self, app, options): # Exceptions for future in self._future_exceptions: - exception_handlers.append(app._apply_exception_handler(future)) + exception_handlers.append( + app._apply_exception_handler(future, route_names) + ) # Event listeners for listener in self._future_listeners: diff --git a/sanic/handlers.py b/sanic/handlers.py index dd1fbac118..460ef64b5d 100644 --- a/sanic/handlers.py +++ b/sanic/handlers.py @@ -1,4 +1,5 @@ from traceback import format_exc +from typing import List, Optional from sanic.errorpages import exception_response from sanic.exceptions import ( @@ -23,15 +24,15 @@ class ErrorHandler: """ - handlers = None - cached_handlers = None + # handlers = None + # cached_handlers = None def __init__(self): self.handlers = [] self.cached_handlers = {} self.debug = False - def add(self, exception, handler): + def add(self, exception, handler, route_names: Optional[List[str]] = None): """ Add a new exception handler to an already existing handler object. @@ -46,9 +47,14 @@ def add(self, exception, handler): """ # self.handlers to be deprecated and removed in version 21.12 self.handlers.append((exception, handler)) - self.cached_handlers[exception] = handler - def lookup(self, exception): + if route_names: + for route in route_names: + self.cached_handlers[(exception, route)] = handler + else: + self.cached_handlers[(exception, None)] = handler + + def lookup(self, exception, route_name: Optional[str]): """ Lookup the existing instance of :class:`ErrorHandler` and fetch the registered handler for a specific type of exception. @@ -63,17 +69,26 @@ def lookup(self, exception): :return: Registered function if found ``None`` otherwise """ exception_class = type(exception) - if exception_class in self.cached_handlers: - return self.cached_handlers[exception_class] - for ancestor in type.mro(exception_class): - if ancestor in self.cached_handlers: - handler = self.cached_handlers[ancestor] - self.cached_handlers[exception_class] = handler + for name in (route_name, None): + exception_key = (exception_class, name) + handler = self.cached_handlers.get(exception_key) + if handler: return handler - if ancestor is BaseException: - break - self.cached_handlers[exception_class] = None + + for name in (route_name, None): + for ancestor in type.mro(exception_class): + exception_key = (ancestor, name) + if exception_key in self.cached_handlers: + handler = self.cached_handlers[exception_key] + self.cached_handlers[ + (exception_class, route_name) + ] = handler + return handler + + if ancestor is BaseException: + break + self.cached_handlers[(exception_class, route_name)] = None handler = None return handler @@ -91,7 +106,9 @@ def response(self, request, exception): :return: Wrap the return value obtained from :func:`default` or registered handler for that type of exception. """ - handler = self.lookup(exception) + route_name = request.name if request else None + handler = self.lookup(exception, route_name) + print(self.cached_handlers, route_name) response = None try: if handler: From 57d3043c399ee56a76e6382a9af5e429da7302d3 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Mon, 2 Aug 2021 01:51:11 +0300 Subject: [PATCH 2/5] sanic-testing version --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index fabe6c28be..6371fe4658 100644 --- a/setup.py +++ b/setup.py @@ -93,7 +93,7 @@ def open_local(paths, mode="r", encoding="utf8"): ] tests_require = [ - "sanic-testing>=0.7.0b1", + "sanic-testing==0.7.0b1", "pytest==5.2.1", "coverage==5.3", "gunicorn==20.0.4", From 07b74c3fe779137d837121a27ca60e1512aae067 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Mon, 2 Aug 2021 08:31:08 +0300 Subject: [PATCH 3/5] Fix test --- tests/test_exceptions_handler.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/tests/test_exceptions_handler.py b/tests/test_exceptions_handler.py index e6fd42eb4f..1c3ccdffc2 100644 --- a/tests/test_exceptions_handler.py +++ b/tests/test_exceptions_handler.py @@ -191,18 +191,24 @@ class ModuleNotFoundError(ImportError): handler.add(CustomError, custom_error_handler) handler.add(ServerError, server_error_handler) - assert handler.lookup(ImportError()) == import_error_handler - assert handler.lookup(ModuleNotFoundError()) == import_error_handler - assert handler.lookup(CustomError()) == custom_error_handler - assert handler.lookup(ServerError("Error")) == server_error_handler - assert handler.lookup(CustomServerError("Error")) == server_error_handler + assert handler.lookup(ImportError(), None) == import_error_handler + assert handler.lookup(ModuleNotFoundError(), None) == import_error_handler + assert handler.lookup(CustomError(), None) == custom_error_handler + assert handler.lookup(ServerError("Error"), None) == server_error_handler + assert ( + handler.lookup(CustomServerError("Error"), None) + == server_error_handler + ) # once again to ensure there is no caching bug - assert handler.lookup(ImportError()) == import_error_handler - assert handler.lookup(ModuleNotFoundError()) == import_error_handler - assert handler.lookup(CustomError()) == custom_error_handler - assert handler.lookup(ServerError("Error")) == server_error_handler - assert handler.lookup(CustomServerError("Error")) == server_error_handler + assert handler.lookup(ImportError(), None) == import_error_handler + assert handler.lookup(ModuleNotFoundError(), None) == import_error_handler + assert handler.lookup(CustomError(), None) == custom_error_handler + assert handler.lookup(ServerError("Error"), None) == server_error_handler + assert ( + handler.lookup(CustomServerError("Error"), None) + == server_error_handler + ) def test_exception_handler_processed_request_middleware(): From cc994070956bea449e45c96f542cfed49605ad12 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Mon, 2 Aug 2021 09:19:46 +0300 Subject: [PATCH 4/5] squash --- sanic/handlers.py | 1 - 1 file changed, 1 deletion(-) diff --git a/sanic/handlers.py b/sanic/handlers.py index 460ef64b5d..63c90dae82 100644 --- a/sanic/handlers.py +++ b/sanic/handlers.py @@ -108,7 +108,6 @@ def response(self, request, exception): """ route_name = request.name if request else None handler = self.lookup(exception, route_name) - print(self.cached_handlers, route_name) response = None try: if handler: From 3c7b7d79e92b8a09567c0eb37acc824913150bc1 Mon Sep 17 00:00:00 2001 From: Adam Hopkins Date: Mon, 2 Aug 2021 11:16:48 +0300 Subject: [PATCH 5/5] Remove dead code --- sanic/handlers.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/sanic/handlers.py b/sanic/handlers.py index 63c90dae82..33ab92f067 100644 --- a/sanic/handlers.py +++ b/sanic/handlers.py @@ -24,9 +24,6 @@ class ErrorHandler: """ - # handlers = None - # cached_handlers = None - def __init__(self): self.handlers = [] self.cached_handlers = {}