click-constrained-option is an extension package that adds constrains to option in Click.
Tested under click 7.x
Build option that:
- mutually exclusive with others
- depend on other options
- is required conditionally
- is promoted conditionally
- set its default value conditionally
- set its type conditionally
pip install click-constrained-option
# example.py
import click
from click_constrained_option import ConstrainedOption
@click.command()
@click.option(cls=ConstrainedOption,
group_require_one=["apple", "orange", "pear"])
@click.option("--apple",
cls=ConstrainedOption,
is_flag=True)
@click.option("--orange",
cls=ConstrainedOption,
is_flag=True)
@click.option("--pear",
cls=ConstrainedOption,
is_flag=True)
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py
Error: require exact one of '--apple' '--orange' '--pear'
$ python example.py --apple --pear
Error: require exact one of '--apple' '--orange' '--pear'
kwarg | type | usage |
---|---|---|
allowed_func | function |
return True to indicate allowance, vice versa |
allowed_if | str |
name of the option |
allowed_if_not | str |
name of the option |
allowed_if_all_of | list |
list of the option names |
allowed_if_none_of | list |
list of the option names |
allowed_if_any_of | list |
list of the option names |
allowed_if_one_of | list |
list of the option names |
required_func | function |
return True to indicate requirement, vice versa |
required_if | str |
name of the option |
required_if_not | str |
name of the option |
required_if_all_of | list |
list of the option names |
required_if_none_of | list |
list of the option names |
required_if_any_of | list |
list of the option names |
required_if_one_of | list |
list of the option names |
prompt_func | function |
return True to indicate prompt, vice versa |
prompt_if | str |
name of the option |
prompt_if_not | str |
name of the option |
prompt_if_all_of | list |
list of the option names |
prompt_if_none_of | list |
list of the option names |
prompt_if_any_of | list |
list of the option names |
prompt_if_one_of | list |
list of the option names |
group_require_one | list |
list of the option names |
group_require_any | list |
list of the option names |
group_require_all | list |
list of the option names |
default_func | function |
return a value to use as the default |
type_func | function |
return a valid type of the option |
-
If multiple kwargs are used, then all of them must be satisfied. For example, if
prompt_if
andprompt_if_all_of
are specified, then the option is prompted only when both condition hold true. -
If the custom function has an argument equals an option name in the command, then the value of that option will be used as argument. For example, value of
--foo
will be passed to a custom functionbar(foo)
.
--username
is allowed if --login
is set:
import click
from click_constrained_option import ConstrainedOption
@click.command()
@click.option("--username",
cls=ConstrainedOption,
allowed_if="login")
@click.option("--login", is_flag=True)
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py --username=foo
Error: Usage for '--username': require '--login'
--login
is allowed if at least one of --userid
and --email
is specified, and --oauth
is not set:
import click
from click_constrained_option import ConstrainedOption
@click.command()
@click.option("--login",
cls=ConstrainedOption,
is_flag=True,
allowed_if_any_of=["userid", "email"],
allowed_if_not="cookie")
@click.option("--userid")
@click.option("--email")
@click.option("--oauth", is_flag=True)
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py --login --userid=foo --oauth
Error: Usage for '--login': conflict with '--oauth'
$ python example.py --login
Error: Usage for '--login': require at least one of '--userid' '--email'
--port
is required if --listen
is an IP address:
import click
import re
from click_constrained_option import ConstrainedOption
@click.command()
@click.option("--port",
cls=ConstrainedOption,
required_func=lambda b: re.match(r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}", b))
@click.option("--listen")
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py --listen=127.0.0.1
Error: Missing option '--port'
Exact one of the --order-by-date
--order-by-name
--order-by-rank
must be set:
import click
from click_constrained_option import ConstrainedOption
@click.option(cls=ConstrainedOption,
group_require_one=["order_by_name", "order_by_rank", "order_by_date"])
@click.option("--order-by-name",
cls=ConstrainedOption,
is_flag=True)
@click.option("--order-by-rank",
cls=ConstrainedOption,
is_flag=True)
@click.option("--order-by-date",
cls=ConstrainedOption,
is_flag=True)
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py
Error: require exact one of '--order-by-name' '--order-by-rank' '--order-by-date'
--password
will be prompted if one of --userid
--email
is specified
import click
from click_constrained_option import ConstrainedOption
@click.command()
@click.option("--userid")
@click.option("--email")
@click.option("--password",
hide_input=True,
prompt_if_one_of=["userid", "email"])
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py --userid
Password:
Type of --time
will be int if --time-format=timestamp
import click
from click_constrained_option import ConstrainedOption
@click.command()
@click.option("--time-format",
type=click.Choice(["iso-8601", "timestamp"]))
@click.option("--time",
cls=ConstrainedOption,
type_func=lambda time_format: click.INT if time_format == "timestamp" else click.STRING)
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python --time-format="timestamp" --time=str_not_int
Error: Invalid value for '--time': str_not_int is not a valid integer
Default of --lucky
is set through custom function
import click
from click_constrained_option import ConstrainedOption
from random import randint
@click.command()
@click.option("--lucky",
default=lambda : randint(0, 9))
def cli(**kwargs):
click.echo(kwargs)
if __name__ == "__main__":
cli()
$ python example.py
{'lucky': '5'}