Skip to content

Commit

Permalink
Subparser fixes: nesting, Optional[]
Browse files Browse the repository at this point in the history
  • Loading branch information
brentyi committed Aug 24, 2022
1 parent 819cc74 commit 2925576
Show file tree
Hide file tree
Showing 4 changed files with 43 additions and 14 deletions.
5 changes: 3 additions & 2 deletions dcargs/_calling.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,16 @@ def get_value_from_arg(prefixed_field_name: str) -> Any:
else:
# Unions over dataclasses (subparsers). This is the only other option.
assert len(parser_definition.subparsers_from_name) > 0
assert field.name in parser_definition.subparsers_from_name
assert prefixed_field_name in parser_definition.subparsers_from_name

subparser_def = parser_definition.subparsers_from_name[field.name]
subparser_def = parser_definition.subparsers_from_name[prefixed_field_name]

subparser_dest = _strings.make_subparser_dest(name=prefixed_field_name)
consumed_keywords.add(subparser_dest)
if subparser_dest in value_from_prefixed_field_name:
subparser_name = get_value_from_arg(subparser_dest)
else:
assert subparser_def.default_instance not in _fields.MISSING_SINGLETONS
default_instance = subparser_def.default_instance
# assert default_instance is not None
subparser_name = None
Expand Down
19 changes: 8 additions & 11 deletions dcargs/_parsers.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,15 +103,13 @@ def from_callable(
not avoid_subparsers
# Required subparsers => must create a subparser.
or subparsers_attempt.required
# If the output can be None, the only way to specify whether this is
# or isn't None is with a subparser.
or subparsers_attempt.can_be_none
):
subparsers_from_name[subparsers_attempt.name] = subparsers_attempt
subparsers_from_name[
_strings.make_field_name([prefix, subparsers_attempt.name])
] = subparsers_attempt
continue
else:
field = dataclasses.replace(field, typ=type(field.default))
assert _fields.is_nested_type(field.typ, field.default)

# (2) Handle nested callables.
if _fields.is_nested_type(field.typ, field.default):
Expand All @@ -129,6 +127,10 @@ def from_callable(
)
args.extend(nested_parser.args)

# Include nested subparsers.
subparsers_from_name.update(nested_parser.subparsers_from_name)

# Include nested strings.
for k, v in nested_parser.helptext_from_nested_class_field_name.items():
helptext_from_nested_class_field_name[
_strings.make_field_name([field.name, k])
Expand Down Expand Up @@ -355,12 +357,7 @@ def apply(
for p in prev_subparser_tree_nodes:
# Add subparsers to every node in previous level of the tree.
argparse_subparsers = p.add_subparsers(
dest=_strings.make_field_name(
[
parent_parser.prefix,
_strings.make_subparser_dest(name=self.name),
]
),
dest=_strings.make_subparser_dest(self.prefix),
description=self.description,
required=self.required,
title=termcolor.colored(title, attrs=["bold"]),
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[tool.poetry]
name = "dcargs"
version = "0.2.1"
version = "0.2.2"
description = "Strongly typed, zero-effort CLI interfaces"
authors = ["brentyi <[email protected]>"]
include = ["./dcargs/**/*"]
Expand Down
31 changes: 31 additions & 0 deletions tests/test_nested.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,3 +697,34 @@ class ChildClass(UnrelatedParentClass, ActualParentClass[int]):
assert dcargs.cli(
ChildClass, args=["--x", "1", "--y", "2", "--z", "3"]
) == ChildClass(x=1, y=2, z=3)


def test_subparser_in_nested():
@dataclasses.dataclass
class A:
a: int

@dataclasses.dataclass
class B:
b: int

@dataclasses.dataclass
class Nested2:
subcommand: Union[A, B]

@dataclasses.dataclass
class Nested1:
nested2: Nested2

@dataclasses.dataclass
class Parent:
nested1: Nested1

assert dcargs.cli(
Parent,
args="nested1.nested2.subcommand:a --nested1.nested2.subcommand.a 3".split(" "),
) == Parent(Nested1(Nested2(A(3))))
assert dcargs.cli(
Parent,
args="nested1.nested2.subcommand:b --nested1.nested2.subcommand.b 7".split(" "),
) == Parent(Nested1(Nested2(B(7))))

0 comments on commit 2925576

Please sign in to comment.