Skip to content

Error Handlers

Error handlers allow you to create modulare, composable, re-usable error handling code for you application.

examples/error_handlers.py
import arc


@arc.command()
def command():
    arc.print("We're going to throw an error")
    raise RuntimeError("Something has gone wrong!")


@command.handle(RuntimeError)
def handle_exception(exc, context):
    arc.print("handled!")


command()
$ python error_handlers.py 
We're going to throw an error
handled!

The handler defined handles RuntimeErrors so it was executed and handled the error. Because there was an error handler registered, no stack trace was printed to the console, and the application exited with a 0 exit code.

Re-Raising

If the handler can't handle a particular error, you can raise the exception again (or even raise a new exception). This will cause it to continue down the list of excetion handlers until it finds one that does handle the exception. If none are found, the exception will be handled by arc's default error-handling behavior.

Relationship to Decorators

Under the hood, exception handlers are decorators. For example, we could take the example from above and convert it to a decorator.

examples/error_handlers_deco.py
import arc


@arc.decorator()
def handle_exception(context):
    try:
        yield
    except RuntimeError:
        arc.print("handled!")


@handle_exception
@arc.command()
def command():
    raise RuntimeError("Something has gone wrong!")


command()
$ python error_handlers_deco.py 
handled!

Error handlers are simply some syntatic sugar on top of decorators to provide a slightly nicer interface. And as such, all of the principles about decorators also apply to error handlers

Like decorators, error handlers are inherited

  • Can be disabled with @command.handle(*exceptions, inherit=False)
  • Can be removed with @handler.remove