Skip to main content

aiohttp-catcher

CI Job PyPI version

aiohttp-catcher is a centralized error handler for aiohttp servers. It enables consistant error handlling across your web server or API, so your code can raise Python exceptions that will be handled however you want them to.



Quickstart

from aiohttp import web
from aiohttp_catcher import catch, Catcher

async def hello(request):
    division = 1 / 0
    return web.Response(text=f"1 / 0 = {division}")


async def main():
    # Add a catcher:
    catcher = Catcher()

    # Register error-handling scenarios:
    await catcher.add_scenario(
        catch(ZeroDivisionError).with_status_code(400).and_return("Zero division makes zero sense")
    )

    # Register your catcher as an aiohttp middleware:
    app = web.Application(middlewares=[catcher.middleware])
    app.add_routes([web.get("/divide-by-zero", hello)])
    web.run_app(app)

Making a request to /divide-by-zero will return a 400 status code with the following body:

{"code": 400, "message": "Zero division makes zero sense"}

Key Features

Return a Constant

In case you want some exceptions to return a constant message across your application, you can do so by using the .and_return("some value") method:

await catcher.add_scenario(
    catch(ZeroDivisionError).with_status_code(400).and_return("Zero division makes zero sense")
)

Stringify the Exception

In some cases, you would want to return a stringified version of your exception, should it entail user-friendly information.

class EntityNotFound(Exception):
    def __init__(self, entity_id, *args, **kwargs):
        super(EntityNotFound, self).__init__(*args, **kwargs)
        self.entity_id = entity_id

    def __str__(self):
        return f"Entity {self.entity_id} could not be found"


@routes.get("/user/{user_id}")
async def get_user(request):
    user_id = request.match_info.get("user_id")
    if user_id not in user_db:
        raise EntityNotFound(entity_id=user_id)
    return user_db[user_id]

# Your catcher can be directed to stringify particular exceptions:

await catcher.add_scenario(
    catch(EntityNotFound).with_status_code(404).and_stringify()
)

Callables and Awaitables

In some cases, you'd want the message returned by your server for some exceptions to call a custom function. This function can either be a synchronous function or an awaitable one. It should expect a single argument, which is the exception being raised:

# Can be a synchronous function as well:
async def write_message(exc: Exception):
    return "Whoops"

await catcher.add_scenarios(
    catch(MyCustomException2).with_status_code(401).and_call(write_message),
    catch(MyCustomException2).with_status_code(403).and_call(lambda exc: str(exc))
)

Handle Several Exceptions Similarly

You can handle several exceptions in the same manner by adding them to the same scenario:

await catcher.add_scenario(
    catch(
        MyCustomException1,
        MyCustomException2,
        MyCustomException3
    ).with_status_code(418).and_return("User-friendly error message")
)

Scenarios as Dictionaries

You can register your scenarios as dictionaries as well:

await catcher.add_scenarios(
    {
        "exceptions": [ZeroDivisionError],
        "constant": "Zero division makes zero sense",
        "status_code": 400,
    },
    {
        "exceptions": [EntityNotFound],
        "stringify_exception": True,
        "status_code": 404,
    },
    {
        "exceptions": [IndexError],
        "func": lambda exc: f"Out of bound: {str(exc)}",
        "status_code": 418,
    },
)

Development

Contributions are warmly welcomed. Before submitting your PR, please run the tests using the following Make target:

make ci

Alternatively, you can run each test suite separately:

  1. Unit tests:

    make test/py
    
  2. Linting with pylint:

    make pylint
    
  3. Static security checks with bandit:

    make pybandit
    

Metadata

Release files for aiohttp-catcher 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for aiohttp-catcher 0.1.0
File Size Uploaded
aiohttp-catcher-0.1.0.tar.gz 4.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for aiohttp-catcher 0.1.0
File Interpreter ABI Platform
aiohttp_catcher-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 9.6 kB

Release files / aiohttp-catcher-0.1.0.tar.gz

Download URL aiohttp-catcher-0.1.0.tar.gz
Size 4.9 kB
Tags Source
SHA-256 checksum
How to use checksums
107dc696942dcbbdce9f1619ee5b8c922896113cf162c4d630ee9fa7485380e9
BLAKE2b-256 checksum
How to use checksums
59718b3c43e3ea17985252638a0ba39fc291dc3fe2ee1642d5c08229fa0d15c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.0.0 CPython/3.8.12 Linux/5.11.0-1022-azure

Release files / aiohttp_catcher-0.1.0-py3-none-any.whl

Download URL aiohttp_catcher-0.1.0-py3-none-any.whl
Size 4.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
317ea467924709e5226bd5627a2e27e5643a76db90f81bd6106d28870b86d078
BLAKE2b-256 checksum
How to use checksums
de502b08f2a50801b6dcfdd224645bf643d92d260a50fe5bc7330ab727f6aa7c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/1.0.0 CPython/3.8.12 Linux/5.11.0-1022-azure

Release history Release notifications | RSS feed

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page