Search code examples
pythonpython-3.xpython-click

display help if incorrect or missing option in click


When I put invalid arguments to a command, only this is displayed:

Usage: ugen.py [OPTIONS]

Error: Missing option "-o" / "--out_file".

I would like to display the whole help message as with --help option

My decorated function:

@click.command(name="ugen")
@click.help_option("-h", "--help")
@click.option(
    "-o", "--out_file",
    help="Output file where data is written.",
    required=True
)
@click.option(
    "-i", "--in_file", multiple=True,
    help=(
        "Input file/s from which data is read. "
        "Can be provided multiple times. "
        "Although always with specifier -i/--in_file."
    ),
    required=True
)
def main(out_file, in_file):
    code here

Solution

  • You can hook the command call, then display the help as desired like:

    Custom Command Class

    import click
    
    class ShowUsageOnMissingError(click.Command):
        def __call__(self, *args, **kwargs):
            try:
                return super(ShowUsageOnMissingError, self).__call__(
                    *args, standalone_mode=False, **kwargs)
            except click.MissingParameter as exc:
                exc.ctx = None
                exc.show(file=sys.stdout)
                click.echo()
                try:
                    super(ShowUsageOnMissingError, self).__call__(['--help'])
                except SystemExit:
                    sys.exit(exc.exit_code)
    

    Using Custom Class

    To use the custom class, just pass the class to the click.command() decorator like:

    @click.command(cls=ShowUsageOnMissingError)
    @click.option("-o", help="Output file where data is written.", required=True)
    def cli(o):
        ...
    

    How does this work?

    This works because click is a well designed OO framework. The @click.command() decorator usually instantiates a click.Command object but allows this behavior to be over-ridden with the cls parameter. So it is a relatively easy matter to inherit from click.Command in our own class and over ride the desired methods.

    In this case, we override __call__() and print the help after printing the exception.

    Test Code

    @click.command(cls=ShowUsageOnMissingError)
    @click.option("-o", help="Output file where data is written.", required=True)
    def cli(o):
        click.echo(o)
    
    
    if __name__ == "__main__":
        commands = (
            '-o outfile',
            '',
            '--help',
        )
    
        import sys, time
    
        time.sleep(1)
        print('Click Version: {}'.format(click.__version__))
        print('Python Version: {}'.format(sys.version))
        for cmd in commands:
            try:
                time.sleep(0.1)
                print('-----------')
                print('> ' + cmd)
                time.sleep(0.1)
                cli(cmd.split(), obj={})
    
            except BaseException as exc:
                if str(exc) != '0' and \
                        not isinstance(exc, (click.ClickException, SystemExit)):
                    raise
    

    Results

    Click Version: 6.7
    Python Version: 3.6.2 (default, Jul 17 2017, 23:14:31)
    [GCC 5.4.0 20160609]
    -----------
    > -o outfile
    outfile
    -----------
    >
    Error: Missing option "-o".
    
    Usage: test.py [OPTIONS]
    
    Options:
      -o TEXT  Output file where data is written.  [required]
      --help   Show this message and exit.
    -----------
    > --help
    Usage: test.py [OPTIONS]
    
    Options:
      -o TEXT  Output file where data is written.  [required]
      --help   Show this message and exit.