Click Command Line Interfaces: make the necessary settings if another option is disabled

When writing a command line interface (CLI) with Python, click on a library to determine if, for example. Three options, where the second and third are needed only if the first (additional) one has not been disabled?

My use case is a login system that allows authentication through either authentication token(option 1) or, alternatively, through username(option 2) and password(option 3).

If a pointer has been given, there is no need to check that usernameboth passwordare defined or called. Otherwise, if the token was omitted, then usernamethey passwordbecome mandatory and must be indicated.

Is there any way to use callbacks?

My code for starters, which, of course, does not reflect the intended template:

@click.command()
@click.option('--authentication-token', prompt=True, required=True)
@click.option('--username', prompt=True, required=True)
@click.option('--password', hide_input=True, prompt=True, required=True)
def login(authentication_token, username, password):
    print(authentication_token, username, password)

if __name__ == '__main__':
    login()
+4
source share
1 answer

This can be done by building your own class obtained from click.Option, and in this class we return to the method click.Option.handle_parse_result(), for example:

Custom class:

import click

class NotRequiredIf(click.Option):
    def __init__(self, *args, **kwargs):
        self.not_required_if = kwargs.pop('not_required_if')
        assert self.not_required_if, "'not_required_if' parameter required"
        kwargs['help'] = (kwargs.get('help', '') +
            ' NOTE: This argument is mutually exclusive with %s' %
            self.not_required_if
        ).strip()
        super(NotRequiredIf, self).__init__(*args, **kwargs)

    def handle_parse_result(self, ctx, opts, args):
        we_are_present = self.name in opts
        other_present = self.not_required_if in opts

        if other_present:
            if we_are_present:
                raise click.UsageError(
                    "Illegal usage: `%s` is mutually exclusive with `%s`" % (
                        self.name, self.not_required_if))
            else:
                self.prompt = None

        return super(NotRequiredIf, self).handle_parse_result(
            ctx, opts, args)

Using custom class:

, cls click.Option decorator, :

@click.option('--username', prompt=True, cls=NotRequiredIf,
              not_required_if='authentication_token')

?

, - OO-. @click.option() click.Option, cls. click.Option .

click.Option.handle_parse_result() user/password, authentication-token, , user/password authentication-token.

.

:

@click.command()
@click.option('--authentication-token')
@click.option('--username', prompt=True, cls=NotRequiredIf,
              not_required_if='authentication_token')
@click.option('--password', prompt=True, hide_input=True, cls=NotRequiredIf,
              not_required_if='authentication_token')
def login(authentication_token, username, password):
    click.echo('t:%s  u:%s  p:%s' % (
        authentication_token, username, password))

if __name__ == '__main__':
    login('--username name --password pword'.split())
    login('--help'.split())
    login(''.split())
    login('--username name'.split())
    login('--authentication-token token'.split())

:

login('--username name --password pword'.split()):

t:None  u:name  p:pword

login('--help'.split()):

Usage: test.py [OPTIONS]

Options:
  --authentication-token TEXT
  --username TEXT              NOTE: This argument is mutually exclusive with
                               authentication_token
  --password TEXT              NOTE: This argument is mutually exclusive with
                               authentication_token
  --help                       Show this message and exit.
+10

All Articles