From 22eaec32ffeff39af799064540f45a91368afb63 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Wed, 20 Nov 2024 06:37:36 +0100 Subject: [PATCH 1/2] The CLASS_PATH_OR_NAME for subclass help is now optional and if not given the help of the base class is printed (#624). --- CHANGELOG.rst | 4 ++++ jsonargparse/_actions.py | 12 ++++++++++-- jsonargparse_tests/test_subclasses.py | 5 ++++- 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index ea3e4678..6dfdf67c 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -20,6 +20,9 @@ Changed - Argument groups created from dataclass-like that have zero configurable arguments no longer adds a config loader (`#634 `__). +- The ``CLASS_PATH_OR_NAME`` for subclass help is now optional and if not given + the help of the base class is printed (`#628 + `__). Deprecated ^^^^^^^^^^ @@ -29,6 +32,7 @@ Deprecated v4.34.1 (2024-12-02) +-------------------- Fixed ^^^^^ diff --git a/jsonargparse/_actions.py b/jsonargparse/_actions.py index 8190c9d2..42327c31 100644 --- a/jsonargparse/_actions.py +++ b/jsonargparse/_actions.py @@ -20,6 +20,7 @@ change_to_path_dir, default_config_option_help, get_import_path, + get_typehint_origin, import_object, indent_text, iter_to_set_str, @@ -346,8 +347,12 @@ def __init__(self, typehint=None, **kwargs): super().__init__(**kwargs) def update_init_kwargs(self, kwargs): - from ._typehints import get_subclass_names + from ._typehints import get_optional_arg, get_subclass_names, get_unaliased_type + typehint = get_unaliased_type(get_optional_arg(self._typehint)) + if get_typehint_origin(typehint) is not Union: + assert "nargs" not in kwargs + kwargs["nargs"] = "?" self._basename = iter_to_set_str(get_subclass_names(self._typehint, callable_return=True)) kwargs.update( { @@ -376,7 +381,10 @@ def print_help(self, call_args): try: typehint = get_unaliased_type(get_optional_arg(self._typehint)) baseclasses = get_subclass_types(typehint, callable_return=True) - val_class = import_object(resolve_class_path_by_name(typehint, value)) + if self.nargs == "?" and value is None: + val_class = typehint + else: + val_class = import_object(resolve_class_path_by_name(typehint, value)) except Exception as ex: raise TypeError(f"{option_string}: {ex}") from ex if not any(is_subclass(val_class, b) for b in baseclasses): diff --git a/jsonargparse_tests/test_subclasses.py b/jsonargparse_tests/test_subclasses.py index d47b6e2c..a29e3431 100644 --- a/jsonargparse_tests/test_subclasses.py +++ b/jsonargparse_tests/test_subclasses.py @@ -548,6 +548,9 @@ def test_subclass_nested_help(parser): help_str = get_parse_args_stdout(parser, [f"--op.help={__name__}.Nested", "--op.cal.help=TextCalendar"]) assert "Help for --op.cal.help=calendar.TextCalendar" in help_str assert "--op.cal.firstweekday" in help_str + help_str = get_parse_args_stdout(parser, [f"--op.help={__name__}.Nested", "--op.cal.help"]) + assert "Help for --op.cal.help=calendar.Calendar" in help_str + assert "--op.cal.firstweekday" in help_str with pytest.raises(ArgumentError) as ctx: parser.parse_args([f"--op.help={__name__}.Nested", "--op.p1=1"]) @@ -1346,7 +1349,7 @@ def test_add_subclass_required_group(parser): parser.add_subclass_arguments(Calendar, "cal", required=True) pytest.raises(ArgumentError, lambda: parser.parse_args([])) help_str = get_parser_help(parser) - assert "[-h] [--cal.help CLASS_PATH_OR_NAME] --cal " in help_str + assert "[-h] [--cal.help [CLASS_PATH_OR_NAME]] --cal " in help_str def test_add_subclass_not_required_group(parser): From 77db689f0c20f5c9a9dea6cc03a599f0b61bdcc4 Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Fri, 22 Nov 2024 21:15:59 +0100 Subject: [PATCH 2/2] Change help of linked arguments --- jsonargparse/_link_arguments.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jsonargparse/_link_arguments.py b/jsonargparse/_link_arguments.py index 0e8aaa33..cfc2b014 100644 --- a/jsonargparse/_link_arguments.py +++ b/jsonargparse/_link_arguments.py @@ -204,7 +204,7 @@ def __init__( help_str: Optional[str] if is_target_subclass and not valid_target_leaf: type_attr = None - help_str = f"Use --{self.target[1].dest}.help CLASS_PATH for details." + help_str = f"Use --{self.target[1].dest}.help for details." else: type_attr = getattr(self.target[1], "_typehint", self.target[1].type) help_str = self.target[1].help