Skip to content
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

Implement flag options #1508

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,32 @@ And on the command line:
invoke(info, args=['--lower'])
invoke(info)

Flag Options
------------

Alternatively, the flag options can be user provided by setting
the ``flag_option=True`` parameter. The default value can be set via the
``flag_value`` parameter.

.. click:example::

@click.command()
@click.option('-S', '--gpg-sign',
flag_option=True, flag_value="default GPG key",
help="Sign the commit with GPG.", metavar='keyid')
def commit(gpg_sign):
if gpg_sign:
click.echo("sign with " + gpg_sign)
else:
click.echo("do not sign commit")

.. click:run::

invoke(commit, args=['-S'])
invoke(commit, args=['--gpg-sign=other_key'])
invoke(commit)
invoke(commit, args=['--help'])

.. _choice-opts:

Choice Options
Expand Down
14 changes: 14 additions & 0 deletions src/click/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -1751,6 +1751,9 @@ class Option(Parameter):
:param flag_value: which value should be used for this flag if it's
enabled. This is set to a boolean automatically if
the option string contains a slash to mark two options.
:param flag_option: if this is set to `True`, then the flag can take an
optional value. Use `flag_value` to define the default
value.
:param multiple: if this is set to `True` then the argument is accepted
multiple times and recorded. This is similar to ``nargs``
in how it works but supports arbitrary number of
Expand All @@ -1775,6 +1778,7 @@ def __init__(
hide_input=False,
is_flag=None,
flag_value=None,
flag_option=False,
multiple=False,
count=False,
allow_from_autoenv=True,
Expand Down Expand Up @@ -1807,6 +1811,7 @@ def __init__(
is_flag = bool(self.secondary_opts)
if is_flag and default_is_missing:
self.default = False
self.flag_option = flag_option if is_flag and flag_value is not None else False
if flag_value is None:
flag_value = not self.default
self.is_flag = is_flag
Expand Down Expand Up @@ -1919,6 +1924,10 @@ def add_to_parser(self, parser, ctx):
parser.add_option(
self.secondary_opts, action=action_const, const=False, **kwargs
)
elif self.flag_option:
parser.add_option(
self.opts, action=action, const=self.flag_value, **kwargs
)
else:
parser.add_option(
self.opts, action=action_const, const=self.flag_value, **kwargs
Expand All @@ -1933,6 +1942,11 @@ def get_help_record(self, ctx):
any_prefix_is_slash = []

def _write_opts(opts):
if self.flag_option:
opts = [
"{}[{}{}]".format(o, "=" if len(o) > 2 else "", self.make_metavar())
for o in opts
]
rv, any_slashes = join_options(opts)
if any_slashes:
any_prefix_is_slash[:] = [True]
Expand Down
5 changes: 5 additions & 0 deletions src/click/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -337,6 +337,8 @@ def _match_long_opt(self, opt, explicit_value, state):
# consumed.
if explicit_value is not None:
state.rargs.insert(0, explicit_value)
elif option.const is not None:
state.rargs.insert(0, option.const)

nargs = option.nargs
if len(state.rargs) < nargs:
Expand Down Expand Up @@ -377,6 +379,9 @@ def _match_short_opt(self, arg, state):
if i < len(arg):
state.rargs.insert(0, arg[i:])
stop = True
elif option.const is not None:
state.rargs.insert(0, option.const)
stop = True

nargs = option.nargs
if len(state.rargs) < nargs:
Expand Down
43 changes: 43 additions & 0 deletions tests/test_basic.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,49 @@ def cli(flag):
assert result.output == "{}\n".format(default)


def test_flag_option(runner):
@click.command()
@click.option("-f", "--flag", flag_option=True, flag_value=0, type=int)
def cli(flag):
click.echo(flag)

result = runner.invoke(cli, ["-f"])
assert not result.exception, result.output
assert result.output == "0\n"
result = runner.invoke(cli, ["-f42"])
assert not result.exception
assert result.output == "42\n"
result = runner.invoke(cli, ["--flag"])
assert not result.exception
assert result.output == "0\n"
result = runner.invoke(cli, ["--flag=42"])
assert not result.exception
assert result.output == "42\n"
result = runner.invoke(cli, [])
assert not result.exception
assert result.output == "\n"

@click.command()
@click.option("-f", "--flag", flag_option=True, flag_value=0, type=int)
@click.argument("arg", default="arg")
def cli(flag, arg):
click.echo(flag)
click.echo(arg)

result = runner.invoke(cli, ["-f"])
assert not result.exception
assert result.output == "0\narg\n"
result = runner.invoke(cli, ["-f", "foo"])
assert not result.exception
assert result.output == "0\nfoo\n"
result = runner.invoke(cli, ["--flag", "bar"])
assert not result.exception
assert result.output == "0\nbar\n"
result = runner.invoke(cli, ["bar"])
assert not result.exception
assert result.output == "\nbar\n"


def test_boolean_conversion(runner):
for default in True, False:

Expand Down