When writing a command-line interface (CLI) with the Python click library, is it possible to define e.g. three options where the second and third one are only required if the first (optional) one was left unset?
My use case is a log-in system which allows me to authenticate either via an authentication token
(option 1), or, alternatively, via username
(option 2) and password
(option 3).
If the token was given, there is no need to check for username
and password
being defined or prompting them. Otherwise, if the token was omitted then username
and password
become required and must be given.
Can this be done somehow using callbacks?
My code to get started which of course does not reflect the intended pattern:
@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()
Slightly improved Stephen Rauch's answer to have multiple mutex parameters.
use like this:
This can be done by building a custom class derived from
click.Option
, and in that class over riding theclick.Option.handle_parse_result()
method like:Custom Class:
Using Custom Class:
To use the custom class, pass the
cls
parameter toclick.option
decorator like:How does this work?
This works because click is a well designed OO framework. The
@click.option()
decorator usually instantiates aclick.Option
object but allows this behavior to be overridden with thecls
parameter. So it is a relatively easy matter to inherit fromclick.Option
in our own class and over ride the desired methods.In this case we over ride
click.Option.handle_parse_result()
and disable the need touser/password
ifauthentication-token
token is present, and complain if bothuser/password
areauthentication-token
are present.Note: This answer was inspired by this answer
Test Code:
Results:
from
login('--username name --password pword'.split())
:from
login('--help'.split())
: