Skip to main content

Utility to create exception handlers in FastAPI in a flexible and standard way

Project description

Trii Logo

Exception Handler [ FastAPI Utility ]

This project works together with FastAPI and allows you to create error handlers in a very simple and standard way. Basically it allows you to add (or use the default a function that will be executed when an exception occurs on the server.

This is possible using the FastAPI Exception Handler (actually Starlette) and using a function as a template that receives data such as the request object that was invoked on the route, the exception, the status code that handles that exception, and some 'static' arguments.

Table of Contents

How to use it

Basic

There are several ways to use it, the simplest is to use it with the templates (Callbacks) that come by default. And simply create a list with the pairs of exceptions and status codes.

from fastapi import FastAPI
from ehandler import ExceptionHandlerSetter

app = FastAPI()

handlers = [(ZeroDivisionError, 500), (ValueError, 400)]
ExceptionHandlerSetter(
    content_callback_kwargs={"show_error": True, "show_data": True}
).add_handlers(app, handlers)


@app.get("/value_error")
def raise_value_error():
    raise ValueError("Invalid value")


@app.get("/division")
def call_external():
    return some_func()


@app.get("/uncaught")
def uncaught():
    raise Exception("This error is not handled")


def some_func():
    print("Trying to do something unless that thing get fails")
    return 1 / 0  # This raise a `ZeroDivisionError`

/value_error - HTTP 400 Bad Request

{
    "detail": "Bad Request",
    "error": "ValueError",
    "message": "Invalid value"
}

/division - HTTP 500 Internal Server Error

{
    "detail": "Internal Server Error",
    "error": "ZeroDivisionError",
    "message": "division by zero"
}

/uncaught - HTTP 500 Internal Server Error

Internal Server Error

Explanation

When an error occurs within our server that is not handled (with a try-except) the server will respond with a 500 - Internal Server Error

ExceptionHandlerSetter().add_handlers(app, handlers) Basically create this to be able to handle specific exceptions (or general if Exception is handled) ( original example ) :

@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
    return JSONResponse(
        status_code=418,
        content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
    )

But with a dynamic/configurable exception and status code and using a function (callback) as a template, in fact 2 functions/callbacks are used:

  • One that parses the content, can take the exception, the request, etc, and create some content with it.
  • The other is the function that returns the response, usually just put it inside a JSONResponse.
@app.exception_handler(<ExceptionClass>)
async def <ResponseCallback>(request: Request, content: Any = <content_callback>, status_code: int = <status_code>)
    return <ContentCallback>,

Examples

Content callback

Use your own callback to parse the content of the response

import traceback

from fastapi import FastAPI
from ehandler import ExceptionHandlerSetter

app = FastAPI()

def parse_exception(
    request: Request,
    exception: Exception,
    status_code: int,
    debug: bool = False,
) -> dict:
    content = {"message": "Something went wrong"}
    if debug:
        content["traceback"] = traceback.format_exc()
    return content

handlers = [(Exception, 500)]
ExceptionHandlerSetter(
    content_callback=parse_exception
    content_callback_kwargs={"debug": True}
).add_handlers(app, handlers)

Add additional data

⚠️ This uses the default implementation from ehandler.parsers.parse_exception

from fastapi import FastAPI
from ehandler import ExceptionHandlerSetter
from ehandler.utils import add_data

app = FastAPI()

handlers = [(Exception, 500)]
ExceptionHandlerSetter(
    content_callback_kwargs={"show_data": True}  # If `show_data` is False it won't work
).add_handlers(app, handlers)


@app.get("/exception")
def raise_value_error():
    raise add_data(ValueError("Invalid value"), {"user": "user_info"})

/exception - HTTP 500 Internal Server Error

{
    "data": {
        "user": "user_info"
    },
    "detail": "Internal Server Error",
    "message": "Invalid value"
}

Change default status_code

from fastapi import FastAPI
from ehandler import ExceptionHandlerSetter
from ehandler.utils import add_code

app = FastAPI()

handlers = [(Exception, 500)]
ExceptionHandlerSetter(force_status_code=False).add_handlers(app, handlers)


@app.get("/exception")
def raise_value_error():
    raise add_code(ValueError("Invalid value"), 400)

/exception - HTTP 400 Bad Request

{
    "detail": "Bad Request",
    "message": "Invalid value"
}

How to contribute

Prerequisites

Python ^3.9

poetry ^1.1.14

pre-commit ^2.20.0

Install requirements

Install Poetry

Poetry is a tool for dependency management and packaging in Python. It allows you to declare the libraries your project depends on and it will manage (install/update) them for you.

To install poetry you can follow the official documentation on the page according to your operating system. Poetry Installation

Install Pre-Commit

It is a multi-language package manager for pre-commit hooks. You specify a list of hooks you want and pre-commit manages the installation and execution of any hook written in any language before every commit.

To install poetry you can follow the official documentation on the page according to your operating system. Pre-Commit Installation

Install hooks

pre-commit install
Dependencies

Install dependencies

poetry install

You can also follow the documentation of poetry for a better use of this or any questions. Poetry Basic Usage

Add dependency

They are the dependencies that the package needs to work.

poetry add <package>
poetry add fastapi

Add dev dependency

These are the dependencies that you need only for development, for example those that are needed to test the package.

poetry add -D <package>
poetry add -D requests
Tests

Run test

Static test

The analysis or static test is run using pre-commit, you can run a specific analysis using the id, or run all tests

Single test
pre-commit run pylint

⚠️ Pre-commit by default only runs on files modified in stage. If you want to run on all files you can add the --all-files flag.

All test
pre-commit run
All files
pre-commit run --all-files
pre-commit run black --all-files

Unittest

poetry run pytest -v

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

ehandler-0.5.1.tar.gz (20.8 kB view hashes)

Uploaded Source

Built Distribution

ehandler-0.5.1-py3-none-any.whl (17.6 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page