From 2bcbd48b2c2de85180b81cd9de8c0b985d49442f Mon Sep 17 00:00:00 2001 From: Mauricio Villegas <5780272+mauvilsa@users.noreply.github.com> Date: Thu, 20 Jun 2024 06:20:16 +0200 Subject: [PATCH] Now --*.help output shows options without init_args (#533) --- CHANGELOG.rst | 9 +++++++++ jsonargparse/_actions.py | 4 ++-- jsonargparse_tests/conftest.py | 6 ++++-- jsonargparse_tests/test_subclasses.py | 18 ++++++++++-------- jsonargparse_tests/test_typehints.py | 2 +- 5 files changed, 26 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a367edae..46a65d79 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,6 +12,15 @@ The semantic versioning only considers the public API as described in paths are considered internals and can change in minor and patch releases. +v4.31.0 (2024-06-??) +-------------------- + +Changed +^^^^^^^ +- Now ``--*.help`` output shows options without ``init_args`` (`#533 + `__). + + v4.30.0 (2024-06-18) -------------------- diff --git a/jsonargparse/_actions.py b/jsonargparse/_actions.py index 5403b071..e681c2d6 100644 --- a/jsonargparse/_actions.py +++ b/jsonargparse/_actions.py @@ -20,6 +20,7 @@ argument_error, change_to_path_dir, default_config_option_help, + get_import_path, get_typehint_origin, import_object, indent_text, @@ -387,8 +388,7 @@ def print_help(self, call_args, baseclass, dest): baseclasses = [baseclass] if not any(is_subclass(val_class, b) for b in baseclasses): raise TypeError(f'{option_string}: Class "{value}" is not a subclass of {self._basename}') - dest += ".init_args" - subparser = type(parser)() + subparser = type(parser)(description=f"Help for {option_string}={get_import_path(val_class)}") subparser.add_class_arguments(val_class, dest, **self.sub_add_kwargs) remove_actions(subparser, (_HelpAction, _ActionPrintConfig, _ActionConfigLoad)) args = self.get_args_after_opt(parser.args) diff --git a/jsonargparse_tests/conftest.py b/jsonargparse_tests/conftest.py index 26ef83b7..54a3c0ac 100644 --- a/jsonargparse_tests/conftest.py +++ b/jsonargparse_tests/conftest.py @@ -27,6 +27,8 @@ set_docstring_parse_options(style=DocstringStyle.GOOGLE) +columns_env = {"COLUMNS": "200"} + is_cpython = platform.python_implementation() == "CPython" is_posix = os.name == "posix" @@ -153,7 +155,7 @@ def source_unavailable(): def get_parser_help(parser: ArgumentParser, strip=False) -> str: out = StringIO() - with patch.dict(os.environ, {"COLUMNS": "200"}): + with patch.dict(os.environ, columns_env): parser.print_help(out) if strip: return re.sub(" *", " ", out.getvalue()) @@ -162,7 +164,7 @@ def get_parser_help(parser: ArgumentParser, strip=False) -> str: def get_parse_args_stdout(parser: ArgumentParser, args: List[str]) -> str: out = StringIO() - with redirect_stdout(out), pytest.raises(SystemExit): + with patch.dict(os.environ, columns_env), redirect_stdout(out), pytest.raises(SystemExit): parser.parse_args(args) return out.getvalue() diff --git a/jsonargparse_tests/test_subclasses.py b/jsonargparse_tests/test_subclasses.py index a3550cb7..60bfb2a7 100644 --- a/jsonargparse_tests/test_subclasses.py +++ b/jsonargparse_tests/test_subclasses.py @@ -156,7 +156,7 @@ def test_subclass_union_help(parser): help_str = get_parser_help(parser) assert "Show the help for the given subclass of Calendar" in help_str help_str = get_parse_args_stdout(parser, ["--op.help", "TextCalendar"]) - assert "--op.init_args.firstweekday" in help_str + assert "--op.firstweekday" in help_str class DefaultsDisabled: @@ -204,8 +204,8 @@ def func_subclass_untyped(c1: Union[int, UntypedParams]): def test_subclass_allow_untyped_parameters_help(parser): parser.add_function_arguments(func_subclass_untyped, fail_untyped=False) help_str = get_parse_args_stdout(parser, [f"--c1.help={__name__}.UntypedParams"]) - assert "--c1.init_args.a1 A1" in help_str - assert "--c1.init_args.a2 A2" in help_str + assert "--c1.a1 A1" in help_str + assert "--c1.a2 A2" in help_str class MergeInitArgs(Calendar): @@ -529,11 +529,12 @@ def test_subclass_nested_parse(parser, prefix): def test_subclass_nested_help(parser): parser.add_argument("--op", type=Nested) - help_str = get_parse_args_stdout(parser, [f"--op.help={__name__}.Nested", "--op.init_args.cal.help=TextCalendar"]) - assert "--op.init_args.cal.init_args.firstweekday" in help_str + 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 with pytest.raises(ArgumentError) as ctx: - parser.parse_args([f"--op.help={__name__}.Nested", "--op.init_args.p1=1"]) + parser.parse_args([f"--op.help={__name__}.Nested", "--op.p1=1"]) ctx.match("Expected a nested --\\*.help option") @@ -580,7 +581,8 @@ def test_subclass_class_name_parse(parser): def test_subclass_class_name_help(parser): parser.add_argument("--op", type=Union[Calendar, GzipFile, None]) help_str = get_parse_args_stdout(parser, ["--op.help=GzipFile"]) - assert "--op.init_args.compresslevel" in help_str + assert "Help for --op.help=gzip.GzipFile" in help_str + assert "--op.compresslevel" in help_str class LocaleTextCalendar(Calendar): @@ -1318,7 +1320,7 @@ def test_add_subclass_tuple(parser): assert isinstance(init.c, TupleBaseB) help_str = get_parse_args_stdout(parser, [f"--c.help={__name__}.TupleBaseB"]) - assert "--c.init_args.b1" in help_str + assert "--c.b1 B1" in help_str def test_add_subclass_required_group(parser): diff --git a/jsonargparse_tests/test_typehints.py b/jsonargparse_tests/test_typehints.py index 2ddf453e..42100836 100644 --- a/jsonargparse_tests/test_typehints.py +++ b/jsonargparse_tests/test_typehints.py @@ -590,7 +590,7 @@ def test_union_new_syntax_simple_types(parser): def test_union_new_syntax_subclass_type(parser): parser.add_argument("--op", type=eval("Calendar | bool")) help_str = get_parse_args_stdout(parser, ["--op.help=calendar.TextCalendar"]) - assert "--op.init_args.firstweekday" in help_str + assert "--op.firstweekday" in help_str # callable tests