Skip to content

Commit

Permalink
Merge 83cc14c into b27709e
Browse files Browse the repository at this point in the history
  • Loading branch information
cbefus committed Dec 12, 2019
2 parents b27709e + 83cc14c commit 509adc8
Show file tree
Hide file tree
Showing 14 changed files with 314 additions and 314 deletions.
124 changes: 124 additions & 0 deletions src/error_handlers_builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import inspect
import logging


from src.internal.plan_execution.error_handlers import ErrorHandlers
from src.internal.plan_execution.default_error_handlers import default_route_not_found_error_handler, \
default_internal_server_error_error_handler, default_internal_server_error_error_handler_only_request, \
default_invalid_cookie_header_error_handler
from src.exceptions import ErrorHandlingBuilderException, RouteNotFoundException, \
CallbackIncorrectNumberOfParametersException, NonCallableExceptionHandlerException, \
InvalidCookieHeaderException


LOG = logging.getLogger("error_handlers_builder")


class ErrorHandlersBuilder:
"""
An object for setting handlers for any exceptions that can come up.
There are two times an error can be thrown in the process of turning a request into a response. Either the
error occurs before we have a response or after. Handlers should be set based on where the exception
is expected (or in both places if it can come up anywhere).
Handling will prefer the most specific exception but will execute against a base exception if one was set.
Several default handlers are set if they are not set manually. The defaults registered
are for RouteNotFound, InvalidCookieHeader, and Exception.
"""

def __init__(self):
self._pre_response_error_handlers = []
self._post_response_error_handler = []

def add_pre_response_error_handler(self, error_class, handler):
"""
Add error handlers which happen before we have built a response (returned from a handler).
:param error_class: The class to execute the handler for.
:param handler: A function which takes a request as a parameter.
:return: This builder so that fluent design can optionally be used.
"""
if not hasattr(handler, '__call__'):
raise NonCallableExceptionHandlerException(
"Pre Response Error Handler for error {e} is not callable.".format(e=str(error_class)))
if 2 != len(inspect.signature(handler).parameters):
raise CallbackIncorrectNumberOfParametersException(
"Pre Response Error Handler for error {e} does not take exactly 2 argument "
"(the error, the request)".format(e=str(error_class)))

if ErrorHandlersBuilder._is_registered_already(error_class, self._pre_response_error_handlers):
raise ErrorHandlingBuilderException(
"Cannot associate error: {e} to more than one handler for pre response errors."
.format(e=str(error_class)))
self._pre_response_error_handlers.append((error_class, handler))
return self

def add_post_response_error_handler(self, error_class, handler):
"""
Add error handlers which happen after we have built a response.
:param error_class: The class to execute the handler for.
:param handler: A function which takes both a request and response parameter.
:return: This builder so that fluent design can optionally be used.
"""
if not hasattr(handler, '__call__'):
raise NonCallableExceptionHandlerException(
"Post Response Error Handler for exception {e} is not callable.".format(e=str(error_class)))
if 3 != len(inspect.signature(handler).parameters):
raise CallbackIncorrectNumberOfParametersException(
"Post Response Error Handler for error {e} does not take exactly 3 argument "
"(the error, the request, the response)".format(e=str(error_class)))

if ErrorHandlersBuilder._is_registered_already(error_class, self._post_response_error_handler):
raise ErrorHandlingBuilderException(
"Cannot associate error: {e} to more than one handler for post response errors."
.format(e=str(error_class)))
self._post_response_error_handler.append((error_class, handler))
return self

def build(self):
"""
set defaults and build the error handlers for setting into the Eynnyd WebAppBuilder.
:return: The ErrorHandlers required by the Eynnyd WebAppBuilder set_error_handlers method.
"""
if not ErrorHandlersBuilder._is_registered_already(
RouteNotFoundException,
self._pre_response_error_handlers):
self.add_pre_response_error_handler(
RouteNotFoundException,
default_route_not_found_error_handler)

if not ErrorHandlersBuilder._is_registered_already(
InvalidCookieHeaderException,
self._pre_response_error_handlers):
self.add_pre_response_error_handler(
InvalidCookieHeaderException,
default_invalid_cookie_header_error_handler)

if not ErrorHandlersBuilder._is_registered_already(
Exception,
self._pre_response_error_handlers):
self.add_pre_response_error_handler(
Exception,
default_internal_server_error_error_handler_only_request)

if not ErrorHandlersBuilder._is_registered_already(
Exception,
self._post_response_error_handler):
self.add_post_response_error_handler(
Exception,
default_internal_server_error_error_handler)

return ErrorHandlers(
self._pre_response_error_handlers,
self._post_response_error_handler)

@staticmethod
def _is_registered_already(exc, error_handlers):
for k, _ in error_handlers:
if k == exc:
return True
return False
124 changes: 0 additions & 124 deletions src/exception_handlers_registry.py

This file was deleted.

8 changes: 4 additions & 4 deletions src/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ class EynnydWebappBuildException(Exception):
pass


class ExceptionHandlingRegisterException(Exception):
class ErrorHandlingBuilderException(Exception):
"""
Raised when there is a problem with registering an exception handler.
Raised when there is a problem with adding an error handler.
"""
pass


class NoGenericExceptionHandlerRegistered(Exception):
class NoGenericErrorHandlerException(Exception):
"""
Raised when attempting to handle an exception and there are no registered handlers (even the base Exception).
Raised when attempting to handle an exception and there are no configured handlers (even the base Exception).
This should not happen.
"""
pass
Expand Down
14 changes: 7 additions & 7 deletions src/eynnyd_webapp_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

from src.exceptions import EynnydWebappBuildException
from src.internal.eynnyd_webapp import EynnydWebapp
from src.exception_handlers_registry import ExceptionHandlersRegistry
from src.error_handlers_builder import ErrorHandlersBuilder


class EynnydWebappBuilder:

def __init__(self):
self._routes = Optional.empty()
self._exception_handlers = ExceptionHandlersRegistry().create()
self._error_handlers = ErrorHandlersBuilder().build()

def set_routes(self, root_tree_node):
"""
Expand All @@ -21,13 +21,13 @@ def set_routes(self, root_tree_node):
self._routes = Optional.of(root_tree_node)
return self

def set_error_handlers(self, exception_handlers):
def set_error_handlers(self, error_handlers):
"""
Sets the error handlers built by Eynnyd ExceptionHandlersRegistry
:param exception_handlers: the result from the Eynnyd ExceptionHandlersRegistry create method
Sets the error handlers built by Eynnyd ErrorHandlersBuilder
:param error_handlers: the result from the Eynnyd ErrorHandlersBuilder create method
:return: This builder so that fluent design can be used
"""
self._exception_handlers = exception_handlers
self._error_handlers = error_handlers
return self

def build(self):
Expand All @@ -39,4 +39,4 @@ def build(self):
return EynnydWebapp(
self._routes.get_or_raise(EynnydWebappBuildException(
"You must set routes for the webapp to route requests too.")),
self._exception_handlers)
self._error_handlers)
10 changes: 5 additions & 5 deletions src/internal/eynnyd_webapp.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@

class EynnydWebapp:

def __init__(self, route_tree, exception_handlers):
def __init__(self, route_tree, error_handlers):
self._route_tree = route_tree
self._exception_handlers = exception_handlers
self._plan_executor = PlanExecutor(self._exception_handlers)
self._error_handlers = error_handlers
self._plan_executor = PlanExecutor(self._error_handlers)

def __call__(self, wsgi_environment, wsgi_start_response): # pragma: no cover
try:
Expand All @@ -34,7 +34,7 @@ def _wsgi_input_to_wsgi_output(self, wsgi_environment): # pragma: no cover
try:
return WSGIResponseAdapter(response_stream_reader).adapt(response)
except Exception as e:
error_response = self._exception_handlers.handle_post_response_error(e, wsgi_loaded_request, response)
error_response = self._error_handlers.handle_post_response_error(e, wsgi_loaded_request, response)
return WSGIResponseAdapter(response_stream_reader).adapt(error_response)

def process_request_to_response(self, wsgi_loaded_request):
Expand All @@ -45,7 +45,7 @@ def process_request_to_response(self, wsgi_loaded_request):
wsgi_loaded_request.http_method,
wsgi_loaded_request.request_uri.path)
except Exception as e:
return self._exception_handlers.handle_pre_response_error(e, wsgi_loaded_request)
return self._error_handlers.handle_pre_response_error(e, wsgi_loaded_request)

updated_request = wsgi_loaded_request.copy_and_set_path_parameters(execution_plan.path_parameters)
return self._plan_executor.execute_plan(execution_plan, updated_request)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from http import HTTPStatus
from src.response_builder import ResponseBuilder

LOG = logging.getLogger("exception_handlers_registry")
LOG = logging.getLogger("default_error_handlers")


def default_route_not_found_exception_handler(exc, request):
def default_route_not_found_error_handler(exc, request):
return ResponseBuilder()\
.set_status(HTTPStatus.NOT_FOUND)\
.set_utf8_body(
Expand All @@ -14,7 +14,7 @@ def default_route_not_found_exception_handler(exc, request):
.build()


def default_invalid_cookie_header_exception_handler(exc, request):
def default_invalid_cookie_header_error_handler(exc, request):
LOG.warning(
"Request attempted with invalid cookie header: {rc}".format(rc=str(request.headers.get("COOKIE"))))
return ResponseBuilder()\
Expand All @@ -25,15 +25,15 @@ def default_invalid_cookie_header_exception_handler(exc, request):
.build()


def default_internal_server_error_exception_handler_only_request(exc, request):
def default_internal_server_error_error_handler_only_request(exc, request):
LOG.exception("Unexpected exception occurred with request {r}.".format(r=request), exc_info=exc)
return ResponseBuilder()\
.set_status(HTTPStatus.INTERNAL_SERVER_ERROR)\
.set_utf8_body("Internal Server Error")\
.build()


def default_internal_server_error_exception_handler(exc, request, response):
def default_internal_server_error_error_handler(exc, request, response):
LOG.exception(
"Unexpected exception occurred with request {q} and response {s}.".format(q=request, s=response), exc_info=exc)
return ResponseBuilder()\
Expand Down

0 comments on commit 509adc8

Please sign in to comment.