Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ Changed
<https://github.com/omni-us/jsonargparse/pull/887>`__).
- Rely on ``required`` attributes to improve compatibility with third-party
argparse extensions (`#890
<https://github.com/omni-us/jsonargparse/pull/890>`__).
<https://github.com/omni-us/jsonargparse/pull/890>`__, `#893
<https://github.com/omni-us/jsonargparse/pull/893>`__).


v4.47.0 (2026-03-13)
Expand Down
3 changes: 2 additions & 1 deletion jsonargparse/_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@
from ._paths import change_to_path_dir
from ._required import (
iter_required_keys,
restore_suppressed_required,
set_required,
suppress_required_actions,
)
Expand Down Expand Up @@ -1369,7 +1370,7 @@ def format_help(self) -> str:
note = f"tried getting defaults considering default_config_files but failed due to: {ex}"
group = self._default_config_files_group
group.description = f"{self._default_config_files}, Note: {note}"
with parser_context(parent_parser=self, defaults_cache=defaults):
with restore_suppressed_required(), parser_context(parent_parser=self, defaults_cache=defaults):
help_str = super().format_help()
return help_str

Expand Down
19 changes: 19 additions & 0 deletions jsonargparse/_required.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
from argparse import Action, _SubParsersAction
from contextlib import contextmanager
from contextvars import ContextVar
from typing import Iterator, Optional, Union

from ._type_checking import ArgumentParser

_suppressed_required_actions: ContextVar[tuple[Action, ...]] = ContextVar("_suppressed_required_actions", default=())


def _iter_required_action_keys(parser: ArgumentParser) -> Iterator[str]:
"""Yields required destinations backed by real argparse actions."""
Expand Down Expand Up @@ -58,6 +61,7 @@ def _find_exact_action(parser: ArgumentParser, key: str) -> Optional[Action]:
def suppress_required_actions(parser: ArgumentParser):
"""Temporarily disables required enforcement on real argparse actions."""
suppressed = []
previously_suppressed = _suppressed_required_actions.get()
visited = set()

def visit(subparser):
Expand All @@ -73,8 +77,23 @@ def visit(subparser):
visit(choice_parser)

visit(parser)
token = _suppressed_required_actions.set(previously_suppressed + tuple(suppressed))
try:
yield
finally:
_suppressed_required_actions.reset(token)
for action in reversed(suppressed):
action.required = True


@contextmanager
def restore_suppressed_required():
"""Temporarily restores required=True for actions suppressed by suppress_required_actions."""
suppressed = _suppressed_required_actions.get()
for action in suppressed:
action.required = True
try:
yield
finally:
for action in suppressed:
action.required = False
8 changes: 7 additions & 1 deletion jsonargparse_tests/test_formatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import pytest

from jsonargparse import ActionParser, ActionYesNo, ArgumentParser
from jsonargparse_tests.conftest import get_parser_help, json_or_yaml_dump
from jsonargparse_tests.conftest import get_parse_args_stdout, get_parser_help, json_or_yaml_dump


@pytest.fixture
Expand Down Expand Up @@ -55,6 +55,12 @@ def test_help_required_and_default(parser):
assert "Option v1. (required, default: v1)" in help_str


def test_help_required_preserved_in_parse_args(parser):
parser.add_argument("--v1", type=int, help="Option v1.", required=True)
help_str = get_parse_args_stdout(parser, ["--help"])
assert "Option v1. (required, type: int)" in help_str


def test_help_type_and_null_default(parser):
parser.add_argument("--v2", type=int, help="Option v2.")
help_str = get_parser_help(parser)
Expand Down
35 changes: 35 additions & 0 deletions jsonargparse_tests/test_signatures.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,41 @@ def test_add_method_parent_classes(parser):
assert added_args == ["m.p2", "m.a1", "m.a2", "m.a3"]


class Model:
pass


class TrainerLike:
def foo(self, model: Model, x: int, y: float = 1.0):
"""Sample extra function.

Args:
model: A model
x: The x
y: The y
"""


@skip_if_docstring_parser_unavailable
def test_add_method_required_argument_in_parse_args_help(parser):
parser.add_method_arguments(TrainerLike, "foo", skip={"model"})

help_str = get_parse_args_stdout(parser, ["--help"])
assert "The x (required, type: int)" in help_str
assert "The y (type: float, default: 1.0)" in help_str


@skip_if_docstring_parser_unavailable
def test_add_method_required_argument_in_subcommand_parse_args_help(parser, subparser):
subparser.description = "Sample extra function:"
subparser.add_method_arguments(TrainerLike, "foo", skip={"model"})
parser.add_subcommands().add_subcommand("foo", subparser)

help_str = get_parse_args_stdout(parser, ["foo", "--help"])
assert "The x (required, type: int)" in help_str
assert "The y (type: float, default: 1.0)" in help_str


# add_function_arguments tests


Expand Down
7 changes: 7 additions & 0 deletions jsonargparse_tests/test_subclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1792,6 +1792,13 @@ def test_subclass_required_parameters_missing(parser):
ctx.match("the following arguments are required: p1, p2")


def test_subclass_help_required_parameters(parser):
parser.add_argument("--op", type=RequiredParamsMissing)
help_str = get_parse_args_stdout(parser, [f"--op.help={__name__}.RequiredParamsMissing"])
assert "(required, type: int)" in help_str
assert "(required, type: str)" in help_str


def test_subclass_get_defaults_lazy_instance(parser):
parser.add_argument("--op", type=RequiredParamsMissing, default=lazy_instance(RequiredParamsMissing, p1=1, p2="x"))
defaults = parser.get_defaults()
Expand Down
Loading