Click's exception handling documentation mentions that certain kinds of exceptions such as Abort
, EOFError
and KeyboardInterrupt
are automatically handled gracefully by the framework.
For the application I'm writing, there are a lot of points from which exceptions could be generated. Terminating the application is the right step, but printing the stack trace isn't. I could always manually do this:
@cli.command()
def somecommand:
try:
# ...
except Exception as e:
click.echo(e)
However, is there a way to have Click handle all exceptions automatically?
In our CLI, all commands are grouped under a single command group. This allowed us to implement some behavior that needed to be executed for each command. One part of that is the exception handling.
Our entry point looks something like this:
@click.group()
@click.pass_context
def entry_point(ctx):
ctx.obj = {"example": "This could be the configuration"}
We use it to run global code, e.g. configure the context
, but you can also define an empty method that does nothing. Other commands can be added to this command group either by using the @entry_point.command()
decorator or entry_point.add_command(cmd)
.
For the exception handling, we wrap the entry_point
in another method that handles the exceptions:
def safe_entry_point():
try:
entry_point()
except Exception as e:
click.echo(e)
In setup.py
, we configure the entry point for the CLI and point it to the wrapper:
entry_points={
'console_scripts': [
'cli = my.package:safe_entry_point'
]
}
The commands of the CLI can be executed through its command group: e.g. cli command
.
There might be more elegant solutions out there, but this is how we solved it. While it introduces a command group as the highest-level element in your CLI, but it allows us do handle all exceptions in a single place without the need to duplicate our error handling in each and every command.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With