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: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ Fixed
- ``dump`` failing when a link target requires serialization and
``skip_link_targets=False`` (`#542
<https://github.com/omni-us/jsonargparse/pull/542>`__).
- ``default_config_files`` making parse fail for subcommands and nested subclass
types (`lightning-forums#5963
<https://lightning.ai/forums/t/problem-lightningcli-with-default-config-files/5963>`__).
- Import path of inherited classmethod not resolving correctly (`lightning#19863
comment
<https://github.com/Lightning-AI/pytorch-lightning/discussions/19863#discussioncomment-10010226>`__).
Expand Down
6 changes: 3 additions & 3 deletions jsonargparse/_actions.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,12 +580,12 @@ def add_prefix(key):
@contextmanager
def parent_parsers_context(key, parser):
prev = parent_parsers.get()
curr = prev + [(key, parser)]
t = parent_parsers.set(curr)
curr = [] if parser is None else prev + [(key, parser)]
token = parent_parsers.set(curr)
try:
yield
finally:
parent_parsers.reset(t)
parent_parsers.reset(token)


class _ActionSubCommands(_SubParsersAction):
Expand Down
3 changes: 2 additions & 1 deletion jsonargparse/_typehints.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
_find_action,
_find_parent_action,
_is_action_value_list,
parent_parsers_context,
remove_actions,
)
from ._common import (
Expand Down Expand Up @@ -453,7 +454,7 @@ def skip_sub_defaults_apply(v):
or (isinstance(v, dict) and any(is_subclass_spec(e) for e in v.values()))
)

with ActionTypeHint.sub_defaults_context():
with ActionTypeHint.sub_defaults_context(), parent_parsers_context(None, None):
parser._apply_actions(cfg, skip_fn=skip_sub_defaults_apply)

@staticmethod
Expand Down
41 changes: 41 additions & 0 deletions jsonargparse_tests/test_subcommands.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import json
import os
import warnings
from pathlib import Path
Expand Down Expand Up @@ -255,6 +256,46 @@ def test_subcommand_required_arg_in_default_config(parser, subparser, tmp_cwd):
assert strip_meta(cfg) == Namespace(output="test", prepare=Namespace(media="test"), subcommand="prepare")


class SubModel:
def __init__(self, p1: int, p2: str = "-"):
pass


class Model:
def __init__(self, submodel: SubModel):
self.submodel = submodel


def test_subcommand_default_config_add_subdefaults(parser, subparser, tmp_cwd):
config = {
"fit": {
"model": {
"class_path": f"{__name__}.Model",
"init_args": {
"submodel": {
"class_path": f"{__name__}.SubModel",
"init_args": {"p1": 1},
}
},
}
}
}
Path("config.json").write_text(json.dumps(config))
parser.default_config_files = ["config.json"]
subcommands = parser.add_subcommands()
subparser = ArgumentParser()
subparser.add_argument("--model", type=Model, required=True)
subcommands.add_subcommand("fit", subparser)
cfg = parser.parse_args([])
assert cfg.fit.model.class_path == f"{__name__}.Model"
assert list(cfg.fit.model.init_args.__dict__.keys()) == ["submodel"]
assert cfg.fit.model.init_args.submodel.class_path == f"{__name__}.SubModel"
assert cfg.fit.model.init_args.submodel.init_args == Namespace(p1=1, p2="-")
init = parser.instantiate_classes(cfg)
assert isinstance(init.fit.model, Model)
assert isinstance(init.fit.model.submodel, SubModel)


def test_subsubcommands_parse_args(subtests):
parser_s1_a = ArgumentParser(exit_on_error=False)
parser_s1_a.add_argument("--os1a", default="os1a_def")
Expand Down