-
Notifications
You must be signed in to change notification settings - Fork 14
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
Mutually exclusive options with the same variable name don't work #11
Comments
@craigds thanks for the report. I will try to investigate this behavior and your solution. I will see what I can do. :) In any case your example looks like a very convenient way to work with mutually exclusive options. Would be great to fix/implement this. |
I was just about to report the same issue. Probably you are aware of it already but in case you are not: The behaviour that "the last option given on command line wins" is actually built into Click itself and does not have to do with click-option-group (I did not read code just tested results of Click-only-code). I am not sure if it is considered a feature in Click or a bug but at least the manual does not point out the intended behaviour exactely: https://click-docs-cn.readthedocs.io/zh_CN/latest/options.html#feature-switches
Without posting further code details, this should best describe Click's behaviour:
other way round:
HTH :-) |
I guess it could work to key them by click-option-group/click_option_group/_core.py Lines 243 to 246 in fba6855
becomes def _option_memo(self, func):
...
self._options[func][option.name, option.flag_value] = option |
I agree the behaviour seems weird at first look, but I don't think this is a bug, or something "broken". So,
which roughly translates to @click.option(
"--output_format",
type=click.Choice(
["text", "json"],
default="text",
case_sensitive=False,
),
) I'm missing how using a mutually exclusive group is required for this case. Also, if |
I had a vary similar issue as well. In my case, I'm doing time range selection, to give you a short example: #!/usr/bin/env python3
from datetime import datetime, timedelta
import click
from click_option_group import optgroup, RequiredMutuallyExclusiveOptionGroup
import iso8601
def parse_timerange(ctx, param, value):
if not value:
return
range_str = value.split("/")
time_range = iso8601.parse_date(range_str[0]), iso8601.parse_date(range_str[1])
return time_range
def parse_last_month(ctx, param, value):
if not value:
return
end_time = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
start_time = (end_time - timedelta(day=1)).replace(day=1)
return start_time, end_time
@click.command()
@optgroup.group("Time selection", cls=RequiredMutuallyExclusiveOptionGroup)
@optgroup.option("--time-range", "time_range", type=click.UNPROCESSED, callback=parse_timerange)
@optgroup.option("--last-month", "time_range", is_flag=True, callback=parse_last_month)
def do_something(time_range: tuple[datetime, datetime]):
pass But I found a workaround by removing variable name and modifying the callback function like this: #!/usr/bin/env python3
from datetime import datetime, timedelta
import click
from click_option_group import optgroup, RequiredMutuallyExclusiveOptionGroup
import iso8601
def parse_timerange(ctx, param, value):
if not value:
return
range_str = value.split("/")
time_range = iso8601.parse_date(range_str[0]), iso8601.parse_date(range_str[1])
ctx["tr"] = time_range # <===== this
return time_range
def parse_last_month(ctx, param, value):
if not value:
return
end_time = datetime.now().replace(day=1, hour=0, minute=0, second=0, microsecond=0)
start_time = (end_time - timedelta(day=1)).replace(day=1)
ctx["tr"] = start_time, end_time # <===== this
return start_time, end_time
@click.command()
@optgroup.group("Time selection", cls=RequiredMutuallyExclusiveOptionGroup)
@optgroup.option("--time-range", type=click.UNPROCESSED, callback=parse_timerange)
@optgroup.option("--last-month", is_flag=True, callback=parse_last_month)
def do_something(
time_range: tuple[datetime, datetime] | None = None,
last_month: bool = False,
tr: tuple[datetime, datetime] | None = None # <===== this
):
if tr is None:
raise click.UsageError("no selected time range") Note that there is a new param This workaround still has some minor issues:
|
If I have two options sharing the same variable name, like this:
The mutual exclusive option group doesn't work - whichever option is specified last on the command line wins:
This may be difficult to solve, but here's my completely custom hack for making it work: koordinates/kart#106
Maybe elements of that solution could be incorporated into this project?
The text was updated successfully, but these errors were encountered: