-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ability to create mutually exclusive option groups. #257
Comments
This is something I'd like as well which is currently not supported. Perhaps it could look something like this:
Or perhaps option group could allow for mutally inclusive options, i.e -foo must be called with -bar. Inclusive seems the sensible default so there could be a param for exclusive such as:
@mitsuhiko thoughts? I'd be happy to put together a PR for this feature. |
+1 |
I don't really see how this works from an API point of view. I would rather see some sort of post validation utility that can check that only one is set or something. Definitely a strong -1 on the |
Not clear on what you mean @mitsuhiko, "utility" in the context of click appears to mean methods like More than happy for you to suggest an API for me to go implement and submit a PR. |
Pretty much yes. I don't see how click would generate a nice looking help page for such things anyways. This gets too complex quickly. For instance some options might only conflict in relation to a specific setting of another option. |
I'm with @mitsuhiko in that I'm not sure how this would work in Click, but for what it's worth I have a similar use case. The full use case is described in #329, but more simply: I have a tool that launches stuff into various cloud providers like AWS, GCE, and so forth. The choice of provider is an option. Depending on what Personally, I don't care that the help page capture these kinds of dependencies between options. (e.g. If I care more that the dependency be captured in the parser (or perhaps in some nice utility method) and that helpful error message be provided. My use case is quite complex, though. Perhaps it is a symptom of a complex design that Click shouldn't try to support. |
Complete overhaul of top-level parsing. Delegation to subcommands using click.group(). Structure of parsing within each of the subcommand is retained, but some of the wrapping code for help functions and version options is no longer needed. Changes to CLI API: - webdav/archive/mirror flags no longer specifiable as mutually exclusive group - argument order in comment is changed to smt comment COMMENT [LABEL] The first of these cannot be fixed without changes to click API (see issue open-research#257 pallets/click#257). The second is a little more annoying from a user perspective. Though it may be fixable given current api. So far, though, I can't figure out how to pass optional arguments before required. I wouldn't be surprised if it isn't possible either. A few associated changes: * Currently using click to print in simplest way possible: click echo, * and for error-like messages specifying err=True to print to standard error. * Because of change, help files look and print slightly (or in some cases majorly) differently Changes to commands.py API: * Breaking changes to the arguments expected by all functions implementing smt subcommands. This is unavoidable with click. Remaining issues : * many test failures due to API changes -- around 67 (filtering calls to parse_arguments from grep -E 'commands\..+(\(|\,)' test_commands.py) lines of the test suite for commands.py will need to be rewritten * test rewrite should be straightforward change from tuples to named args * migration to click doesn't intend to change downstream behavior so passing tests can serve as a check on rewrite * docs not updated to modified look of help pages
@nchammas have you found a solution for dynamically making |
@alanhamlett - Not yet, but when I have the time I am planning to write a utility function that captures simple dependencies like this. If it works well for my use case I will post it here as a proposed solution to this issue. |
I just used a callback function for now: |
👍 |
I'm going to close this. There is no likely API for this and you can already manually check on this in callbacks. |
I don't know if we are talking about the same thing, I don't understand as I use callback functions to create mutually exclusive option groups, and I am not familiar with the click's code, but I have rehearsed the following code for my project: https://github.com/OSMBrasil/paicemana/blob/master/paicemana/clickexclusive.py |
You can store data on |
The programation of the callback functions can become very complicated with many parameters. I am in favor of something using annotations, but I can not to perform the changes. |
I don't think it's complicated at all. |
How would be an example for |
Are you talking about the problematic formatting of such options in the help page? Why not use an option of type Choice? On 23 July 2015 17:59:10 CEST, Alexandre Magno [email protected] wrote:
Sent from my phone. Please excuse my brevity. |
Parameters of different meanings, names, types and requirements. |
Do you know of any pracical example that features so many mutually exclusive parameters with different types? On 23 July 2015 18:20:04 CEST, Alexandre Magno [email protected] wrote:
Sent from my phone. Please excuse my brevity. |
OSMBrasil/paicemana#15. See the |
I strongly suspect those should be implemented as subcommands, though I'm not really trusting Google Translate. |
Semantically I prefer to reserve the universe of commands for program expansion. Anyway, I think this is a decision very "personal" and that the click should not impose such restriction. But I know that I can use Choice options from refactoring... Let's say I or other interest in studying and implementing something like the #257 (comment) example, would you be open to receive pull requests in this regard? |
I don't know what you mean by that sentence. I was thinking about a command-line like this:
Click imposes such restrictions all the time. Limiting ones "personal freedoms" is Click's spirit, so to speak, to achieve a certain consistency across command-line applications. |
Sorry, I had not given me of that |
Like this:
|
Oh! This is very good. Thank you very much. I haven't found this at documentation. Update Now I am reading here: |
@untitaker, is possible to show a full help (commands and subcommands)? |
No, not at the moment, feel free to open a new issue. |
Quick update for people: I put together a few utility methods for my project Flintrock that enforce option dependencies like we've been discussing here. The utility methods help you enforce the following kinds of requirements:
I've added these utility methods to my project in this PR: nchammas/flintrock#74 Here are a few example invocations to illustrate: option_requires(
option='--install-hdfs',
requires_all=['--hdfs-version'],
scope=locals())
option_requires(
option='--install-spark',
requires_any=[
'--spark-version',
'--spark-git-commit'],
scope=locals())
mutually_exclusive(
options=[
'--spark-version',
'--spark-git-commit'],
scope=locals())
option_requires(
option='--provider',
conditional_value='ec2',
requires_all=[
'--ec2-key-name',
'--ec2-identity-file',
'--ec2-instance-type',
'--ec2-region',
'--ec2-ami',
'--ec2-user'],
scope=locals()) This is pretty hacky with the passing of I'm not proposing this for addition to Click -- I don't think this API would work for most people -- but I thought I'd share my work since others have been looking for a way to enforce these kinds of requirements. |
I needed to do this too. Here's what I came up with; not perfect (for each option, you need to give the list of other conflicting options; there's no nice global definition), but it works well enough for what I need. https://gist.github.com/jacobtolar/fb80d5552a9a9dfc32b12a829fa21c0c Example usage: @command(help="Run the command.")
@option('--jar-file', cls=MutuallyExclusiveOption, help="The jar file the topology lives in.", mutually_exclusive=["other_arg"])
@option('--other-arg', cls=MutuallyExclusiveOption, help="Another argument.", mutually_exclusive=["jar_file"])
def cli(jar_file, other_arg):
print "Running cli."
print "jar-file: {}".format(jar_file)
print "other-arg: {}".format(other_arg) |
I think this topic should be addressed in the official docs (and state why Click does not support this feature), under the "Choice Options" (http://click.pocoo.org/6/options/#choice-options) heading, because the idea is very similar. |
@jacobtolar FWIW I made this revision that allows you to just define a single list of allowed choices for each option. https://gist.github.com/listx/e06c7561bddfe47346e41a23a3026f33 The usage remains identical, except instead of passing in disparate lists for each argument, you pass in the same list each time. |
I just hacked together a quick and dirty, differently-styled solution to this problem, which makes it easy to specify multiple different exclusive relationships. |
But what about options that are required depending on the value of another option? "3" requires security context information and passphrases like -l -u -a -A -x -X Any idea how to implement that with click? |
For finding this post and wondering if there is a better solution for simple mutex options here is my slightly changed version from https://stackoverflow.com/questions/44247099/click-command-line-interfaces-make-options-required-if-other-optional-option-is using a custom class to validate the options: import click
class Mutex(click.Option):
def __init__(self, *args, **kwargs):
self.not_required_if:list = kwargs.pop("not_required_if")
assert self.not_required_if, "'not_required_if' parameter required"
kwargs["help"] = (kwargs.get("help", "") + "Option is mutually exclusive with " + ", ".join(self.not_required_if) + ".").strip()
super(Mutex, self).__init__(*args, **kwargs)
def handle_parse_result(self, ctx, opts, args):
current_opt:bool = self.name in opts
for mutex_opt in self.not_required_if:
if mutex_opt in opts:
if current_opt:
raise click.UsageError("Illegal usage: '" + str(self.name) + "' is mutually exclusive with " + str(mutex_opt) + ".")
else:
self.prompt = None
return super(Mutex, self).handle_parse_result(ctx, opts, args) Use it like this: @click.group()
@click.option("--username", prompt=True, cls=Mutex, not_required_if=["token"])
@click.option("--password", prompt=True, hide_input=True, cls=Mutex, not_required_if=["token"])
@click.option("--token", cls=Mutex, not_required_if=["username","password"])
def login(ctx=None, username:str=None, password:str=None, token:str=None) -> None:
print("...do what you like with the params you got...") |
I have created the project: https://github.com/espdev/click-option-group |
@mitsuhiko what do you think about such API? |
Perhaps I'm missing something, but I can't find a way to create mutually exclusive option groups in Click. For example, I have a command that can take input from either a socket or a file, and I'd like the following syntax:
At most one (or, in other examples, exactly one) of the options may be specified. Specifying more than one is a parse error caught by the library.
Is it possible to implement this in click at present? If not, would it be feasible to add it? I've not quite got my head around the general architecture yet. Would this feature be a reasonable fit, or does it go against the overall design in some way I've missed?
The text was updated successfully, but these errors were encountered: