Skip to content

Commit 54bb07d

Browse files
authored
Deprecate enable_path in favor of sub_config(s) to improve naming consistency (#907)
1 parent 4d8afe6 commit 54bb07d

8 files changed

Lines changed: 126 additions & 27 deletions

File tree

CHANGELOG.rst

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,15 @@ Deprecated
4747
- ``Path.get_content`` is deprecated and will be removed in v5.0.0. Instead use
4848
``Path.read_text`` for text and ``Path.open`` for binary content (`#906
4949
<https://github.com/omni-us/jsonargparse/pull/906>`__).
50+
- ``enable_path`` parameter of :meth:`add_argument
51+
<.ArgumentParser.add_argument>` was deprecated and will be removed in
52+
v5.0.0. Use ``sub_configs`` instead, which is consistent with the
53+
``sub_configs`` parameter of the signature methods,
54+
:meth:`add_class_arguments <.ArgumentParser.add_class_arguments>` etc.
55+
(`#907 <https://github.com/omni-us/jsonargparse/pull/907>`__).
56+
- ``enable_path`` parameter of :class:`.ActionJsonSchema` was deprecated and
57+
will be removed in v5.0.0. Use ``sub_config`` instead (`#907
58+
<https://github.com/omni-us/jsonargparse/pull/907>`__).
5059

5160

5261
v4.48.0 (2026-04-10)

DOCUMENTATION.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ An argument with a path type can be given ``nargs='+'`` to parse multiple paths.
658658
Thus, from command line you could do ``--files file1 file2``, separated by
659659
space. It might also be desired to parse a list of paths found in a plain text
660660
file or from stdin. For this add the argument with type ``list[<path_type>]``
661-
and ``enable_path=True``. To read from stdin give the special string ``'-'``.
661+
and ``sub_configs=True``. To read from stdin give the special string ``'-'``.
662662
Example:
663663

664664
.. testsetup:: path_list
@@ -685,7 +685,7 @@ Example:
685685

686686
from jsonargparse.typing import Path_fr
687687

688-
parser.add_argument("--list", type=list[Path_fr], enable_path=True)
688+
parser.add_argument("--list", type=list[Path_fr], sub_configs=True)
689689
cfg = parser.parse_args(["--list", "paths.lst"]) # File with list of paths
690690
cfg = parser.parse_args(["--list", "-"]) # List of paths from stdin
691691

@@ -706,7 +706,7 @@ automatically created from type hints in signatures, that is with
706706

707707
.. note::
708708

709-
If ``nargs='+'`` and ``enable_path=True`` are set for an argument of type
709+
If ``nargs='+'`` and ``sub_configs=True`` are set for an argument of type
710710
``list[<path_type>]``, each argument will produce a list of paths. This
711711
behavior may not be what you expect.
712712

jsonargparse/_core.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,21 @@ def __init__(self, *args, **kwargs) -> None:
116116
self.register("action", "parsers", ActionSubCommands)
117117
self.register("action", "config", ActionConfigFile)
118118

119-
def add_argument(self, *args, enable_path: bool = False, **kwargs):
119+
def add_argument(self, *args, sub_configs: bool = False, **kwargs):
120120
"""Adds an argument to the parser or argument group.
121121
122122
All the arguments from `argparse.ArgumentParser.add_argument
123123
<https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser.add_argument>`_
124124
are supported. Additionally it accepts:
125125
126126
Args:
127-
enable_path: Whether to try parsing path/subconfig when argument is a complex type.
127+
sub_configs: Whether to try parsing a sub-config when argument is a complex type.
128128
"""
129+
from ._deprecated import add_argument_enable_path_deprecation
130+
131+
deprecated_val = add_argument_enable_path_deprecation(kwargs)
132+
if deprecated_val is not None:
133+
sub_configs = deprecated_val
129134
parser = self.parser if hasattr(self, "parser") else self
130135
if kwargs.get("action") is not None:
131136
if ActionParser._is_valid_action_parser(parser, kwargs["action"]):
@@ -134,13 +139,13 @@ def add_argument(self, *args, enable_path: bool = False, **kwargs):
134139
if "type" in kwargs:
135140
if is_subclasses_disabled(kwargs["type"]):
136141
nested_key = args[0].lstrip("-")
137-
self.add_class_arguments(kwargs.pop("type"), nested_key, **kwargs)
142+
self.add_class_arguments(kwargs.pop("type"), nested_key, sub_configs=sub_configs, **kwargs)
138143
return find_action(parser, nested_key)
139144
if ActionTypeHint.is_supported_typehint(kwargs["type"]):
140145
args = ActionTypeHint.prepare_add_argument(
141146
args=args,
142147
kwargs=kwargs,
143-
enable_path=enable_path,
148+
enable_path=sub_configs,
144149
container=super(),
145150
logger=self._logger,
146151
)

jsonargparse/_deprecated.py

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@
4040
"usage_and_exit_error_handler",
4141
]
4242

43+
_message_add_argument_enable_path = """
44+
``enable_path`` parameter of ``add_argument`` was deprecated in v4.49.0 and will be removed in v5.0.0.
45+
Use ``sub_configs`` instead.
46+
"""
47+
48+
_message_action_json_schema_enable_path = """
49+
``enable_path`` parameter of ``ActionJsonSchema`` was deprecated in v4.49.0 and will be removed in v5.0.0.
50+
Use ``sub_config`` instead.
51+
"""
52+
4353

4454
shown_deprecation_warnings: Set[Any] = set()
4555

@@ -97,6 +107,38 @@ def decorated(*args, **kwargs):
97107
return deprecated_decorator
98108

99109

110+
def add_argument_enable_path_deprecation(kwargs: dict, stacklevel: int = 1) -> Optional[bool]:
111+
"""Handle deprecated ``enable_path`` parameter in ``add_argument``.
112+
113+
If ``enable_path`` is present in kwargs, emit a deprecation warning and
114+
return its value (popping it from kwargs). Returns ``None`` if not present.
115+
"""
116+
if "enable_path" in kwargs:
117+
deprecation_warning(
118+
add_argument_enable_path_deprecation,
119+
_message_add_argument_enable_path,
120+
stacklevel=stacklevel + 1,
121+
)
122+
return kwargs.pop("enable_path")
123+
return None
124+
125+
126+
def action_json_schema_enable_path_deprecation(kwargs: dict, stacklevel: int = 1) -> Optional[bool]:
127+
"""Handle deprecated ``enable_path`` parameter in ``ActionJsonSchema``.
128+
129+
If ``enable_path`` is present in kwargs, emit a deprecation warning and
130+
return its value (popping it from kwargs). Returns ``None`` if not present.
131+
"""
132+
if "enable_path" in kwargs:
133+
deprecation_warning(
134+
action_json_schema_enable_path_deprecation,
135+
_message_action_json_schema_enable_path,
136+
stacklevel=stacklevel + 1,
137+
)
138+
return kwargs.pop("enable_path")
139+
return None
140+
141+
100142
def parse_as_dict_patch():
101143
"""Adds parse_as_dict support to ArgumentParser as a patch.
102144
@@ -263,7 +305,7 @@ def __call__(self, *args, **kwargs):
263305

264306
@deprecated("""
265307
ActionPathList was deprecated in v4.20.0 and will be removed in v5.0.0. Instead
266-
use as type ``List[<path_type>]`` with ``enable_path=True``.
308+
use as type ``List[<path_type>]`` with ``sub_configs=True``.
267309
""")
268310
class ActionPathList(Action):
269311
"""Action to check and store a list of file paths read from a plain text file or stream."""

jsonargparse/_jsonschema.py

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,19 +21,24 @@ class ActionJsonSchema(Action):
2121
"""Action to parse option as JSON validated by a JSON Schema."""
2222

2323
def __init__(
24-
self, schema: Optional[Union[str, dict]] = None, enable_path: bool = True, with_meta: bool = True, **kwargs
24+
self, schema: Optional[Union[str, dict]] = None, sub_config: bool = True, with_meta: bool = True, **kwargs
2525
):
2626
"""Initializer for ActionJsonSchema instance.
2727
2828
Args:
2929
schema: Schema to validate values against.
30-
enable_path: Whether to try to load JSON from path.
30+
sub_config: Whether to try to load JSON from path.
3131
with_meta: Whether to include metadata.
3232
3333
Raises:
3434
ValueError: If a parameter is invalid.
3535
jsonschema.exceptions.SchemaError: If the schema is invalid.
3636
"""
37+
from ._deprecated import action_json_schema_enable_path_deprecation
38+
39+
deprecated_val = action_json_schema_enable_path_deprecation(kwargs)
40+
if deprecated_val is not None:
41+
sub_config = deprecated_val
3742
if schema is not None:
3843
if isinstance(schema, str):
3944
mode = "yaml" if pyyaml_available else "json"
@@ -45,13 +50,13 @@ def __init__(
4550
jsonvalidator = import_jsonschema("ActionJsonSchema")[1]
4651
jsonvalidator.check_schema(schema)
4752
self._validator = self._extend_jsonvalidator_with_default(jsonvalidator)(schema)
48-
self._enable_path = enable_path
53+
self._sub_config = sub_config
4954
self._with_meta = with_meta
5055
elif "_validator" not in kwargs:
5156
raise ValueError("Expected schema keyword argument.")
5257
else:
5358
self._validator = kwargs.pop("_validator")
54-
self._enable_path = kwargs.pop("_enable_path")
59+
self._sub_config = kwargs.pop("_sub_config")
5560
self._with_meta = kwargs.pop("_with_meta")
5661
super().__init__(**kwargs)
5762

@@ -63,7 +68,7 @@ def __call__(self, *args, **kwargs):
6368
"""
6469
if len(args) == 0:
6570
kwargs["_validator"] = self._validator
66-
kwargs["_enable_path"] = self._enable_path
71+
kwargs["_sub_config"] = self._sub_config
6772
kwargs["_with_meta"] = self._with_meta
6873
if "help" in kwargs and isinstance(kwargs["help"], str) and "%s" in kwargs["help"]:
6974
import json
@@ -83,7 +88,7 @@ def _check_type(self, value):
8388
value = [value]
8489
for num, val in enumerate(value):
8590
try:
86-
val, fpath = parse_value_or_config(val, enable_path=self._enable_path)
91+
val, fpath = parse_value_or_config(val, enable_path=self._sub_config)
8792
path_meta = val.pop("__path__") if isinstance(val, dict) and "__path__" in val else None
8893
self._validator.validate(val)
8994
if path_meta is not None:

jsonargparse_tests/test_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -815,7 +815,7 @@ def test_save_multifile_list(parser, tmp_cwd):
815815
nargs="+",
816816
type=ListItem,
817817
required=True,
818-
enable_path=True,
818+
sub_configs=True,
819819
)
820820

821821
cfg = parser.parse_args([f"--config={main_file_in}"])

jsonargparse_tests/test_deprecated.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from jsonargparse import (
2020
CLI,
2121
ActionJsonnet,
22+
ActionJsonSchema,
2223
ArgumentError,
2324
ArgumentParser,
2425
Namespace,
@@ -62,6 +63,7 @@
6263
responses_activate,
6364
skip_if_docstring_parser_unavailable,
6465
skip_if_fsspec_unavailable,
66+
skip_if_jsonschema_unavailable,
6567
skip_if_requests_unavailable,
6668
skip_if_responses_unavailable,
6769
)
@@ -1086,3 +1088,39 @@ def test_compose_dataclasses():
10861088
)
10871089
assert 2 == len(dataclasses.fields(ComposeAB))
10881090
assert {"a": 3, "b": "2"} == dataclasses.asdict(ComposeAB(a=2, b="2")) # pylint: disable=unexpected-keyword-arg
1091+
1092+
1093+
def test_add_argument_enable_path_deprecated(parser, tmp_cwd):
1094+
import json
1095+
1096+
data = {"a": 1}
1097+
pathlib.Path("data.yaml").write_text(json.dumps(data))
1098+
1099+
with catch_warnings(record=True) as w:
1100+
parser.add_argument("--data", type=dict, enable_path=True)
1101+
assert_deprecation_warn(
1102+
w,
1103+
message="``enable_path`` parameter of ``add_argument`` was deprecated",
1104+
code='parser.add_argument("--data", type=dict, enable_path=True)',
1105+
)
1106+
cfg = parser.parse_args(["--data=data.yaml"])
1107+
path_value = cfg["data"].pop("__path__")
1108+
assert "data.yaml" == str(path_value)
1109+
assert data == cfg["data"]
1110+
1111+
1112+
@skip_if_jsonschema_unavailable
1113+
def test_action_json_schema_enable_path_deprecated(parser):
1114+
1115+
schema = {"type": "object", "properties": {"x": {"type": "integer"}}}
1116+
1117+
with catch_warnings(record=True) as w:
1118+
action = ActionJsonSchema(schema=schema, enable_path=False)
1119+
assert_deprecation_warn(
1120+
w,
1121+
message="``enable_path`` parameter of ``ActionJsonSchema`` was deprecated",
1122+
code="ActionJsonSchema(schema=schema, enable_path=False)",
1123+
)
1124+
parser.add_argument("--obj", action=action)
1125+
cfg = parser.parse_args(['--obj={"x": 1}'])
1126+
assert cfg.obj == {"x": 1}

jsonargparse_tests/test_paths.py

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -553,14 +553,14 @@ def test_path_fr_default_stdin(parser):
553553
assert defaults.path == Path_fr("-")
554554

555555

556-
# enable_path tests
556+
# sub_configs tests
557557

558558

559-
def test_enable_path_dict(parser, tmp_cwd):
559+
def test_sub_configs_dict(parser, tmp_cwd):
560560
data = {"a": 1, "b": 2, "c": [3, 4]}
561561
pathlib.Path("data.yaml").write_text(json.dumps(data))
562562

563-
parser.add_argument("--data", type=Dict[str, Any], enable_path=True)
563+
parser.add_argument("--data", type=Dict[str, Any], sub_configs=True)
564564
cfg = parser.parse_args(["--data=data.yaml"])
565565
assert "data.yaml" == str(cfg["data"].pop("__path__"))
566566
assert data == cfg["data"]
@@ -569,17 +569,17 @@ def test_enable_path_dict(parser, tmp_cwd):
569569
ctx.match("Expected a path but does-not-exist.yaml either not accessible or invalid")
570570

571571

572-
def test_enable_path_subclass(parser, tmp_cwd):
572+
def test_sub_configs_subclass(parser, tmp_cwd):
573573
cal = {"class_path": "calendar.Calendar"}
574574
pathlib.Path("cal.yaml").write_text(json.dumps(cal))
575575

576-
parser.add_argument("--cal", type=Calendar, enable_path=True)
576+
parser.add_argument("--cal", type=Calendar, sub_configs=True)
577577
cfg = parser.parse_args(["--cal=cal.yaml"])
578578
init = parser.instantiate(cfg)
579579
assert isinstance(init["cal"], Calendar)
580580

581581

582-
def test_enable_path_list_path_fr(parser, tmp_cwd, mock_stdin, subtests):
582+
def test_sub_configs_list_path_fr(parser, tmp_cwd, mock_stdin, subtests):
583583
tmpdir = tmp_cwd / "subdir"
584584
tmpdir.mkdir()
585585
(tmpdir / "file1").touch()
@@ -599,13 +599,13 @@ def test_enable_path_list_path_fr(parser, tmp_cwd, mock_stdin, subtests):
599599
parser.add_argument(
600600
"--list",
601601
type=List[Path_fr],
602-
enable_path=True,
602+
sub_configs=True,
603603
)
604604
parser.add_argument(
605605
"--lists",
606606
nargs="+",
607607
type=List[Path_fr],
608-
enable_path=True,
608+
sub_configs=True,
609609
)
610610

611611
with subtests.test("paths list from file"):
@@ -653,15 +653,15 @@ def test_enable_path_list_path_fr(parser, tmp_cwd, mock_stdin, subtests):
653653

654654
@pytest.mark.parametrize("validate_defaults", [False, True])
655655
@patch_parsing_settings
656-
def test_enable_path_list_path_fr_default_stdin(parser, tmp_cwd, validate_defaults, mock_stdin, subtests):
656+
def test_sub_configs_list_path_fr_default_stdin(parser, tmp_cwd, validate_defaults, mock_stdin, subtests):
657657
set_parsing_settings(validate_defaults=validate_defaults)
658658
(tmp_cwd / "file1").touch()
659659
(tmp_cwd / "file2").touch()
660660

661661
parser.add_argument(
662662
"--list",
663663
type=List[Path_fr],
664-
enable_path=True,
664+
sub_configs=True,
665665
default="-",
666666
)
667667

@@ -712,11 +712,11 @@ def __init__(self, path: Optional[os.PathLike] = None):
712712
pass
713713

714714

715-
def test_enable_path_optional_pathlike_subclass_parameter(parser, tmp_cwd):
715+
def test_sub_configs_optional_pathlike_subclass_parameter(parser, tmp_cwd):
716716
data_path = pathlib.Path("data.json")
717717
data_path.write_text('{"a": 1}')
718718

719-
parser.add_argument("--data", type=DataOptionalPath, enable_path=True)
719+
parser.add_argument("--data", type=DataOptionalPath, sub_configs=True)
720720

721721
cfg = parser.parse_args([f"--data={__name__}.DataOptionalPath", f"--data.path={data_path}"])
722722
assert cfg.data.class_path == f"{__name__}.DataOptionalPath"

0 commit comments

Comments
 (0)