Skip to content

Commit

Permalink
refactor: Pull handling of --no-* bool arguments out of the parser.
Browse files Browse the repository at this point in the history
  • Loading branch information
DanCardin committed Sep 16, 2024
1 parent 0883969 commit e9ebe64
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 12 deletions.
33 changes: 32 additions & 1 deletion src/cappa/arg.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ def value_actions(cls) -> typing.Set[ArgAction]:
def is_custom(cls, action: ArgAction | Callable | None):
return action is not None and not isinstance(action, ArgAction)

@property
def is_bool_action(self):
return self in {self.store_true, self.store_false}


@dataclasses.dataclass(order=True)
class Group:
Expand Down Expand Up @@ -192,7 +196,7 @@ def collect(
)
result.append(normalized_arg)

return result
return list(explode_negated_bool_args(result))

def normalize(
self,
Expand Down Expand Up @@ -606,6 +610,33 @@ def infer_value_name(arg: Arg, field_name: str, num_args: int | None) -> str:
return field_name


def explode_negated_bool_args(args: typing.Sequence[Arg]) -> typing.Iterable[Arg]:
"""Expand `--foo/--no-foo` solo arguments into dual-arguments.
Acts as a transform from `Arg(long='--foo/--no-foo')` to
`Annotated[Arg(long='--foo', action=ArgAction.store_true), Arg(long='--no-foo', action=ArgAction.store_false)]`
"""
for arg in args:
yielded = False
if isinstance(arg.action, ArgAction) and arg.action.is_bool_action and arg.long:
long = typing.cast(list[str], arg.long)

negatives = [item for item in long if "--no-" in item]
positives = [item for item in long if "--no-" not in item]
positive_arg = dataclasses.replace(
arg, long=positives, action=ArgAction.store_true
)
negative_arg = dataclasses.replace(
arg, long=negatives, action=ArgAction.store_false
)
yield positive_arg
yield negative_arg
yielded = True

if not yielded:
yield arg


no_extra_arg_actions = {
ArgAction.store_true,
ArgAction.store_false,
Expand Down
5 changes: 0 additions & 5 deletions src/cappa/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -342,11 +342,6 @@ def join_help(*segments):
def get_action(arg: Arg) -> argparse.Action | type[argparse.Action] | str:
action = arg.action
if isinstance(action, ArgAction):
if action in {ArgAction.store_true, ArgAction.store_false}:
long = assert_type(arg.long, list)
has_no_option = any("--no-" in i for i in long)
if has_no_option:
return BooleanOptionalAction
return action.value

action = typing.cast(Callable, action)
Expand Down
7 changes: 1 addition & 6 deletions src/cappa/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -625,12 +625,7 @@ class Value(typing.Generic[T]):


def store_bool(val: bool):
def store(arg: Arg, option: RawOption):
long = assert_type(arg.long, list)
has_no_option = any("--no-" in i for i in long)
if has_no_option:
return not option.name.startswith("--no-")

def store():
return val

return store
Expand Down

0 comments on commit e9ebe64

Please sign in to comment.