Scripts and CI rely on exit codes, and users rely on clean error messages instead of tracebacks.
In standalone mode (the default when you call cli()), Command.main wraps everything:
click.ClickExceptionand subclasses are printed nicely (Error: ...) and exit with theirexit_code: 1 forClickException, 2 forUsageErrorandBadParameter(which also print the usage line and a "Try --help" hint).click.Abort(raised on Ctrl+C at a prompt or when a confirmation is declined) printsAborted!and exits 1.click.exceptions.Exit(code)/ctx.exit(code)exits with that code silently.KeyboardInterruptbecomesAbort, andEOFErrorat a prompt too.- A broken pipe on stdout (
mytool | head) is handled quietly. - Any other exception propagates with a normal traceback.
So raise click.ClickException("config file is invalid") for expected errors, and subclass it to set a custom exit_code (for example 3 for "not found") or to override show().
With standalone_mode=False, Click does not print or exit: main() returns the command's return value, and exceptions propagate (except Exit, whose code is returned). This is how you embed a Click command in another program or test the raw behaviour. UsageError still carries .ctx and format_message() for your own reporting.
Command callbacks' return values are ignored in standalone mode unless you use result_callback on groups, which receives the subcommands' return values, useful for pipelines with chain=True.
Example
import click
class NotFound(click.ClickException):
exit_code = 3
@click.command()
@click.argument("key")
def get(key):
data = {"a": 1}
if key not in data:
raise NotFound(f"no such key: {key}")
return data[key]
for args in (["a"], ["zzz"], []):
try:
get.main(args, prog_name="get") # standalone mode
except SystemExit as e:
print("exit code", e.code)
# exit code 0
# Error: no such key: zzz (stderr)
# exit code 3
# Usage: get [OPTIONS] KEY ... Error: Missing argument 'KEY'. (stderr)
# exit code 2
print(get.main(["a"], standalone_mode=False)) # 1 (return value, no exit)Remember
- ClickException exits 1, UsageError/BadParameter exit 2, Abort prints 'Aborted!'
- Subclass ClickException to set custom exit codes
- standalone_mode=False returns values and propagates exceptions
Watch out for
- Calling sys.exit or printing tracebacks for expected user errors instead of raising ClickException.