Can the usage string itself be modified according to constraints? #79
-
A brief non-sequitur To begin I want to express that I feel this project is both super useful and highly necessary--excellent work! Recently, after deciding to use Click to implement a CLI, and structuring my project around its command decorators, I was shocked to find that input constraints are completely unsupported. When reading about all the things that Click is capable of, I had taken for granted that this would be a core, basic feature of any CLI library. I nearly decided to jump ship entirely and restructure my project around another library when I noticed. Luckily, before doing so, I did some googling and came across this project. Don't get me wrong, I think Click is an excellent project, but this omission made it entirely unusable to me. This My Question I'm fairly new to both Click and Cloup, so I apologize if this question already has an obvious answer in either library. To the point, if I have a CLI with a help text that looks like this (pulled from the docs):
Can I transform the usage string into something like this?
I can see from this example alone why something like this might be considered ambiguous: though the usage string clearly indicates the mutually exclusive constraint, it fails to convey the "at least 1" constraint. That being said, the latter constraint is still clearly enumerated elsewhere in the help text. As a result, I don't personally consider this a deal-breaker, and find it consistent with what I've come to expect from help texts in some common utility programs. Is such a usage string possible to implement with Cloup? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Thank you for the nice words in the beginning. Let me clarify that this is not a fork, it's more something in between a set of integrated Click "extensions" and a Click wrapper. Coming to your question, I've never customized the usage string myself. I think that in case of commands with many options, it's quite pointless to generate an exhaustive usage string: a better alternative is to provide usage examples in the command help text and those should probably be manually written. For simple commands, you may consider to use the To make everything automatic you need to subclass from typing import List
import click
from click import Context
import cloup
from cloup.constraints import mutually_exclusive
# Rather than subclassing Option and overriding get_usage_pieces, I prefer
# to use a function here.
def get_usage_pieces(option, ctx: Context) -> List[str]:
return list(option.get_help_record(ctx)[:1])
class UsageCommand(cloup.Command):
def collect_usage_pieces(self, ctx: Context) -> List[str]:
rv = []
for param in self.get_params(ctx):
if isinstance(param, click.Argument):
rv += param.get_usage_pieces(ctx)
opt_groups = self.option_groups + [self.get_default_option_group(ctx)]
for grp in opt_groups:
if grp.constraint is mutually_exclusive:
pieces = []
for opt in grp.options:
pieces.append(' '.join(get_usage_pieces(opt, ctx)))
grp_usage = f"[{' | '.join(pieces)}]"
rv.append(grp_usage)
else:
for opt in grp.options:
pieces = get_usage_pieces(opt, ctx)
if pieces:
opt_usage = f"[{' '.join(pieces)}]"
rv.append(opt_usage)
return rv
@cloup.command(cls=UsageCommand)
@cloup.argument('arg')
@cloup.option_group(
'Group',
cloup.option('-a'),
cloup.option('-b'),
constraint=mutually_exclusive,
)
@cloup.option('-c')
def f(**kwargs):
pass
if __name__ == '__main__':
f() which has the following usage string:
The above code is a quite simplistic solution but it should give you at least a starting point on how to implement the feature in your program. At the moment, I don't think this feature should be added to Cloup, as I don't have in mind any clean API for it. I'm open to proposals though. |
Beta Was this translation helpful? Give feedback.
Thank you for the nice words in the beginning. Let me clarify that this is not a fork, it's more something in between a set of integrated Click "extensions" and a Click wrapper.
Coming to your question, I've never customized the usage string myself. I think that in case of commands with many options, it's quite pointless to generate an exhaustive usage string: a better alternative is to provide usage examples in the command help text and those should probably be manually written.
For simple commands, you may consider to use the
options_metavar
argument of command and manually specify what you want to see in place of the default[OPTIONS]
. Of course, this is not ideal.To make everything a…