Search code examples
pythonpython-click

python click subcommand unified error handling


In the case where there are command groups and every sub-command may raise exceptions, how can I handle them all together in one place?

Given the example below:

import click


@click.group()
def cli():
    pass

@cli.command()
def foo():
    pass

if __name__ == '__main__':
    cli()

Both cli and foo may raise. I know that one possible solution is to place try-except around cli() in the if clause. But that does not work when you distribute a package. In setup.py, you have to specify an entry point (in this case, cli). The if clause will not be executed.


Solution

  • You can create a custom click.Group by inheriting from same. The custom group can be used by passing it as the cls parameter to the click.group() decorator. If you override the __call__ method, you can insert an exception handler like:

    Code:

    class CatchAllExceptions(click.Group):
    
        def __call__(self, *args, **kwargs):
            try:
                return self.main(*args, **kwargs)
            except Exception as exc:
                click.echo('We found %s' % exc)
    

    Test Code:

    import click
    
    @click.group(cls=CatchAllExceptions)
    def cli():
        pass
    
    @cli.command()
    def foo():
        raise Exception('an exception!')
    
    if __name__ == '__main__':
        cli('foo'.split())
    

    Results:

    We found an exception!