Skip to content

Commit

Permalink
Merge pull request #25 from explosion/feature/bool-negation
Browse files Browse the repository at this point in the history
  • Loading branch information
ines committed Apr 14, 2023
2 parents 53e12cd + 3e68d35 commit 70644fc
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 5 deletions.
23 changes: 23 additions & 0 deletions radicli/tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -985,3 +985,26 @@ def test(a: str, b: str = "1", *, c: int = 1, d: bool = False, e: str = "yo"):
args = ["hello", "--c", "1", "--d"]
parsed = cli.parse(args, cli.commands["test"])
assert parsed == {"a": "hello", "c": 1, "d": True}


def test_cli_booleans():
cli = Radicli()

@cli.command(
"test",
a=Arg("--a"),
b=Arg("--b"),
c=Arg("--c"),
)
def test(a: bool, b: bool = False, c: bool = True):
...

args = ["--a", "--b", "--c"]
parsed = cli.parse(args, cli.commands["test"])
assert parsed == {"a": True, "b": True, "c": True}
args = []
parsed = cli.parse(args, cli.commands["test"])
assert parsed == {"a": False, "b": False, "c": True}
args = ["--no-c"]
parsed = cli.parse(args, cli.commands["test"])
assert parsed == {"a": False, "b": False, "c": False}
46 changes: 42 additions & 4 deletions radicli/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,46 @@ def __init__(self, arg_id: str, message: str):
super().__init__(self.message)


# This is included with argparse in Python 3.9+ and above, but we're also
# supporting 3.8 so the action is inlined here
class BooleanOptionalAction(argparse.Action):
def __init__(
self,
option_strings,
dest,
default=None,
type=None,
choices=None,
required=False,
help=None,
metavar=None,
):
_option_strings = []
for option_string in option_strings:
_option_strings.append(option_string)
if option_string.startswith("--"):
option_string = "--no-" + option_string[2:]
_option_strings.append(option_string)
super().__init__(
option_strings=_option_strings,
dest=dest,
nargs=0,
default=default,
type=type,
choices=choices,
required=required,
help=help,
metavar=metavar,
)

def __call__(self, parser, namespace, values, option_string=None):
if option_string is not None and option_string in self.option_strings:
setattr(namespace, self.dest, not option_string.startswith("--no-"))

def format_usage(self) -> str:
return " | ".join(self.option_strings)


@dataclass
class Arg:
"""Field for defining the CLI argument in the decorator."""
Expand Down Expand Up @@ -306,10 +346,8 @@ def get_arg(
f"boolean arguments need to be flags, e.g. --{arg.id.replace('_', '-')}",
)
arg.type = None
if default is True:
raise InvalidArgumentError(arg.id, "boolean flags need to default to False")
arg.default = False
arg.action = "store_true"
arg.default = False if default is not True else True
arg.action = "store_true" if arg.default is False else BooleanOptionalAction
return arg
if inspect.isclass(param_type) and issubclass(param_type, Enum):
arg.choices = list(param_type.__members__.keys())
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[metadata]
version = 0.0.21
version = 0.0.22
description = Radically lightweight command-line interfaces
url = https://github.com/explosion/radicli
author = Explosion
Expand Down

0 comments on commit 70644fc

Please sign in to comment.