Skip to content

Commit

Permalink
Fix confusing error message when CLI is used with a class that define…
Browse files Browse the repository at this point in the history
…s a subcommand method (#430 comment).
  • Loading branch information
mauvilsa committed Feb 1, 2024
1 parent c5999b1 commit 4ea9922
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 7 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ Fixed
^^^^^
- ``argcomplete`` not working when type and choices given (`#442
<https://github.com/omni-us/jsonargparse/issues/442>`__).
- Confusing error message when ``CLI`` is used with a class that defines a
``subcommand`` method (`#430 comment
<https://github.com/omni-us/jsonargparse/issues/430#issuecomment-1903974112>`__).


v4.27.3 (2024-01-26)
Expand Down
2 changes: 2 additions & 0 deletions jsonargparse/_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -604,6 +604,8 @@ def add_subcommand(self, name, parser, **kwargs):
"""
if parser._subparsers is not None:
raise ValueError("Multiple levels of subcommands must be added in level order.")
if self.dest == name:
raise ValueError(f"A subcommand name can't be the same as the subcommands dest: '{name}'.")

parser.prog = f"{self._prog_prefix} [options] {name}"
parser.env_prefix = f"{self.env_prefix}{name}_"
Expand Down
15 changes: 8 additions & 7 deletions jsonargparse/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,23 +151,24 @@ def _add_subcommands(
def _add_component_to_parser(component, parser, as_positional, fail_untyped, config_help):
kwargs = dict(as_positional=as_positional, fail_untyped=fail_untyped, sub_configs=True)
if inspect.isclass(component):
subcommand_keys = [k for k, v in inspect.getmembers(component) if callable(v) and k[0] != "_"]
if not subcommand_keys:
class_methods = [k for k, v in inspect.getmembers(component) if callable(v) and k[0] != "_"]
if not class_methods:
added_args = parser.add_class_arguments(component, as_group=False, **kwargs)
if not parser.description:
parser.description = get_help_str(component, parser.logger)
return added_args
added_args = parser.add_class_arguments(component, **kwargs)
subcommands = parser.add_subcommands(required=True)
for key in subcommand_keys:
description = get_help_str(getattr(component, key), parser.logger)
for method in class_methods:
method_object = getattr(component, method)
description = get_help_str(method_object, parser.logger)
subparser = type(parser)(description=description)
subparser.add_argument("--config", action=ActionConfigFile, help=config_help)
added_subargs = subparser.add_method_arguments(component, key, as_group=False, **kwargs)
added_args += [f"{key}.{a}" for a in added_subargs]
added_subargs = subparser.add_method_arguments(component, method, as_group=False, **kwargs)
added_args += [f"{method}.{a}" for a in added_subargs]
if not added_subargs:
remove_actions(subparser, (ActionConfigFile, _ActionPrintConfig))
subcommands.add_subcommand(key, subparser, help=get_help_str(getattr(component, key), parser.logger))
subcommands.add_subcommand(method, subparser, help=get_help_str(method_object, parser.logger))
else:
added_args = parser.add_function_arguments(component, as_group=False, **kwargs)
if not parser.description:
Expand Down
11 changes: 11 additions & 0 deletions jsonargparse_tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,17 @@ def test_unexpected_components(components):
CLI(components)


class ConflictingSubcommandKey:
def subcommand(self, x: int = 0):
return x


def test_conflicting_subcommand_key():
with pytest.raises(ValueError) as ctx:
CLI(ConflictingSubcommandKey, args=["subcommand", "--x=1"])
assert ctx.match("subcommand name can't be the same")


# single function tests


Expand Down

0 comments on commit 4ea9922

Please sign in to comment.