Skip to content

Exception

Default exception handlers

Exceptions can be intercepted and converted to a response with a status code. By default, if debug is enabled (by default), the exception will be captured and the server will response with error 500 with a detailed error description containing the error, the url, the headers and the stack trace:

{
  "error": "list index out of range",
  "url": "/items/444",
  "headers": {
    "wsgi.input": "<_io.BufferedReader name=7>",
    "wsgi.errors": "<_io.TextIOWrapper name='<stderr>' mode='w' encoding='UTF-8'>",
    "wsgi.version": "(1, 0)",
    "wsgi.run_once": "False",
    "wsgi.multithread": "True",
    "wsgi.multiprocess": "False",
    "wsgi.file_wrapper": "<class 'wsgiref.util.FileWrapper'>"
  },
  "stack_trace": [
    "Traceback (most recent call last):",
    "  File \"/Users/fdang/chimpler/pytcher/pytcher/app.py\", line 124, in _handle_request",
    "    None",
    "  File \"/Users/fdang/chimpler/pytcher/pytcher/app.py\", line 117, in <genexpr>",
    "    output",
    "  File \"/Users/fdang/chimpler/pytcher/pytcher/app.py\", line 120, in <genexpr>",
    "    for router in self._routers",
    "  File \"/Users/fdang/chimpler/pytcher/pytcher/__init__.py\", line 151, in run_router",
    "    return router.func(request, *matched_vars)",
    "  File \"examples/simple_app.py\", line 22, in route",
    "    return self._items[item_id]",
    "IndexError: list index out of range",
    ""
  ]
}

If debug is disabled (for example in production), a simple Internal error message will be returned:

{
  "error": "Internal Error"
}

Customize exception handlers

If one wants to intercept other exceptions and not use the default ones, one can create a function or a method inside a class and use the decorator handle_exception with the exception to capture.

The following example captures the ValueError exceptions and return a response with the exception message.

from pytcher import Request, handle_exception

@handle_exception(ValueError)
def handle_value_error(request: Request, exception: ValueError):
    return {
        "error": str(exception)
    }

And then pass it to the application App:

from pytcher import App

app = App([router, handle_value_error])
app.run()

Note that it can also be defined in the same class that defines the route:

from pytcher import App, Request, route, handle_exception

class MyRoute(object):
    @route(path='/items')
    def route(self, r: Request):
        return "Hello"

    @handle_exception(ValueError)
    def handle_value_error(self, request: Request, exception: ValueError):
        return {
            "error": str(exception)
        }

app = App(MyRoute())
app.run()