Skip to content

Instantly share code, notes, and snippets.

@thebopshoobop
Last active December 3, 2021 07:13
Show Gist options
  • Save thebopshoobop/51c4b6dce31017e797699030e3975dbf to your computer and use it in GitHub Desktop.
Save thebopshoobop/51c4b6dce31017e797699030e3975dbf to your computer and use it in GitHub Desktop.
A simple utility function for enforcing mutual exclusivity between click options.
$ python single_context_mutually_exclusive.py -a
$ python single_context_mutually_exclusive.py -b
$ python single_context_mutually_exclusive.py -a -b
Usage: single_context_mutually_exclusive.py [OPTIONS]
Error: options a, b, and c are mutually exclusive
$ python single_context_mutually_exclusive.py -b -c
Usage: single_context_mutually_exclusive.py [OPTIONS]
Error: options a, b, and c are mutually exclusive
$ python single_context_mutually_exclusive.py -a -b -c
Usage: single_context_mutually_exclusive.py [OPTIONS]
Error: options a, b, and c are mutually exclusive
$ python multiple_context_mutually_exclusive.py -a
$ python multiple_context_mutually_exclusive.py -a command -b
Usage: multiple_context_mutually_exclusive.py.py command [OPTIONS]
Error: command option b may not be specified with application option a
$ python multiple_context_mutually_exclusive.py -a command -c
$ python multiple_context_mutually_exclusive.py command -c -d
$ python multiple_context_mutually_exclusive.py command -b -c
Usage: multiple_context_mutually_exclusive.py.py command [OPTIONS]
Error: command option c may not be specified with command option b
import click
def exclusive(ctx_params, exclusive_params, error_message):
if sum([1 if ctx_params[p] else 0 for p in exclusive_params]) > 1:
raise click.UsageError(error_message)
@click.group()
@click.option('-a', is_flag=True)
@click.pass_context
def cli(ctx, a):
pass
@cli.command()
@click.option('-b', is_flag=True)
@click.option('-c', is_flag=True)
@click.option('-d', is_flag=True)
@click.pass_context
def command(ctx, b, c, d):
exclusive(ctx.params, ['b', 'c'], 'command option c may not be specified with command option b')
exclusive(ctx.params, ['b', 'd'], 'command option d may not be specified with command option b')
exclusive({**ctx.params, **ctx.parent.params}, ['a', 'b'], 'command option b may not be specified with application option a')
"""
Note: {**ctx.params, **ctx.parent.params} is a python3.5+ idiom for merging dictionaries. If you are using a previous version, you can do something like:
ctx_params = ctx.params.copy()
ctx_params.update(ctx.parent.params)
then pass ctx_params dictionary to exclusive()
Also Note:
Since dictionaries retain the last-defined value for each key, this function will be unreliable or non-functional if you reuse option names in different contexts.
"""
if __name__ == '__main__':
cli()
import click
def exclusive(ctx_params, exclusive_params, error_message):
if sum([1 if ctx_params[p] else 0 for p in exclusive_params]) > 1:
raise click.UsageError(error_message)
@click.command()
@click.option('-a', is_flag=True)
@click.option('-b', is_flag=True)
@click.option('-c', is_flag=True)
def example_command(a, b, c):
exclusive(click.get_current_context().params, ['a', 'b', 'c'], 'options a, b, and c are mutually exclusive')
if __name__ == '__main__':
example_command()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment