mgross
mgross

Reputation: 652

Python Click: How to make an option available only when another option has a certain value?

I want to enhance an existing command-line application done with Python click to allow for certain options depending on what the --format option is set to.

In my special case, I would like to enable --delimiter option when the --format option equals csv and an --indent option when the --format option equals json. It should all fit into one command, so I do not want to introduce subcommands if not absolutely necessary.

I looked into the group mechanism of click and also the additional extension package click-option-group but I think this is only about grouping the commands, but does not fulfill the goal above.

This question is similar: python-click: dependent options on another option, but the difference is that it has predefined values for the "sub option", whereas in my case --indent is a user-specified value, e.g. 2, 4, 1, et cetera... Furthermore, as all the answers that go into my direction are rather old, I would look for a more up-to-date answer, as to what is possible as of today. What also would be great if it were possible to use functionality of a Python library with preference on click instead of having to add additional code outside of the library.

Thanks for your help.

Upvotes: 1

Views: 1811

Answers (2)

arman
arman

Reputation: 352

You can use the cloup library like this:

import cloup
from cloup.constraints import (
    If, require_one, Equal
)

@cloup.command(show_constraints=True)
@click.option('-f', '--format', required=True,
              type=click.Choice(['csv', 'json']))
@click.option('-d', '--delimiter', required=False, type=click.Choice(['\t', ', ']))
@click.option('-i', '--indent', required=False, type=int)
@cloup.constraint(If(Equal('format', 'csv'), then=require_one.hidden()), ['delimiter'])
@cloup.constraint(If(Equal('format', 'json'), then=require_one.hidden()), ['indent'])
def formatter(format, delimiter, indent):
    pass

Upvotes: 2

wankata
wankata

Reputation: 989

You can use a callback function like this:

def fix_required(ctx, param, value):
    if value == 'csv':
        ctx.command.params[1].required = True
    else:
        ctx.command.params[2].required = True

@click.command()
@click.option('-f', '--format', required=True,
              type=click.Choice(['csv', 'json']), callback=fix_required)
@click.option('-i', '--delimiter', required=False, type=click.Choice(['\t', ', ']))
@click.option('-i', '--indent', required=False, type=int)
def formatter(format, delimiter, indent):
    pass

The params list is ordered after the order of the option annotations, so it is predictable and should be kept up to date if you add options to your command or edit their order.

Upvotes: 1

Related Questions