diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 335bbb5c..e3ff2375 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -22,6 +22,13 @@ Changed for the corresponding parser/subcommand (`#809 `__). +Deprecated +^^^^^^^^^^ +- ``ArgumentParser.default_meta`` property and ``with_meta`` parameter of + ``ArgumentParser.parse_*`` are deprecated and will be removed in v5.0.0. + Instead use ``.clone(with_meta=...)`` (`#810 + `__). + v4.43.0 (2025-11-11) -------------------- diff --git a/jsonargparse/_cli.py b/jsonargparse/_cli.py index 8775be96..20f50b15 100644 --- a/jsonargparse/_cli.py +++ b/jsonargparse/_cli.py @@ -91,7 +91,7 @@ def auto_cli( if unexpected: raise ValueError(f"Unexpected components, not class or function: {unexpected}") - parser = parser_class(default_meta=False, **kwargs) + parser = parser_class(**kwargs) parser.add_argument("--config", action=ActionConfigFile, help=config_help) if not isinstance(components, (list, dict)): diff --git a/jsonargparse/_core.py b/jsonargparse/_core.py index 0dde003a..bedf2458 100644 --- a/jsonargparse/_core.py +++ b/jsonargparse/_core.py @@ -242,7 +242,6 @@ def __init__( dump_header: Optional[list[str]] = None, default_config_files: Optional[list[Union[str, os.PathLike]]] = None, default_env: bool = False, - default_meta: bool = True, **kwargs, ) -> None: """Initializer for ArgumentParser instance. @@ -261,7 +260,6 @@ def __init__( dump_header: Header to include as comment when dumping a config object. default_config_files: Default config file locations, e.g. ``['~/.config/myapp/*.yaml']``. default_env: Set the default value on whether to parse environment variables. - default_meta: Set the default value on whether to include metadata in config objects. """ super().__init__(*args, formatter_class=formatter_class, logger=logger, **kwargs) self._group_class = get_argument_group_class(self) @@ -271,7 +269,6 @@ def __init__( self.required_args: set[str] = set() self.save_path_content: set[str] = set() self.default_config_files = default_config_files - self.default_meta = default_meta self.default_env = default_env self.env_prefix = env_prefix self.parser_mode = parser_mode @@ -340,7 +337,6 @@ def _parse_common( cfg: Namespace, env: Optional[bool], defaults: bool, - with_meta: Optional[bool], skip_validation: bool, skip_required: bool = False, skip_subcommands: bool = False, @@ -352,7 +348,6 @@ def _parse_common( cfg: The configuration object. env: Whether to merge with the parsed environment, None to use parser's default. defaults: Whether to merge with the parser's defaults. - with_meta: Whether to include metadata in config object, None to use parser's default. skip_validation: Whether to skip validation of configuration. skip_required: Whether to skip check of required arguments. skip_subcommands: Whether to skip subcommand processing. @@ -387,9 +382,6 @@ def _parse_common( if not skip_validation: self.validate(cfg, skip_required=skip_required) - if not (with_meta or (with_meta is None and self._default_meta)): - cfg = cfg.clone(with_meta=False) - return cfg def _parse_defaults_and_environ( @@ -417,7 +409,6 @@ def parse_args( # type: ignore[override] namespace: Optional[Namespace] = None, env: Optional[bool] = None, defaults: bool = True, - with_meta: Optional[bool] = None, **kwargs, ) -> Namespace: """Parses command line argument strings. @@ -430,7 +421,6 @@ def parse_args( # type: ignore[override] args: List of arguments to parse or None to use sys.argv. env: Whether to merge with the parsed environment, None to use parser's default. defaults: Whether to merge with the parser's defaults. - with_meta: Whether to include metadata in config object, None to use parser's default. Returns: A config object with all parsed values. @@ -465,7 +455,6 @@ def parse_args( # type: ignore[override] cfg=cfg, env=env, defaults=defaults, - with_meta=with_meta, skip_validation=skip_validation, ) @@ -481,7 +470,6 @@ def parse_object( cfg_base: Optional[Namespace] = None, env: Optional[bool] = None, defaults: bool = True, - with_meta: Optional[bool] = None, **kwargs, ) -> Namespace: """Parses configuration given as an object. @@ -490,7 +478,6 @@ def parse_object( cfg_obj: The configuration object. env: Whether to merge with the parsed environment, None to use parser's default. defaults: Whether to merge with the parser's defaults. - with_meta: Whether to include metadata in config object, None to use parser's default. Returns: A config object with all parsed values. @@ -513,7 +500,6 @@ def parse_object( cfg=cfg, env=env, defaults=defaults, - with_meta=with_meta, skip_validation=skip_validation, skip_required=skip_required, ) @@ -558,7 +544,6 @@ def parse_env( self, env: Optional[dict[str, str]] = None, defaults: bool = True, - with_meta: Optional[bool] = None, **kwargs, ) -> Namespace: """Parses environment variables. @@ -566,7 +551,6 @@ def parse_env( Args: env: The environment object to use, if None `os.environ` is used. defaults: Whether to merge with the parser's defaults. - with_meta: Whether to include metadata in config object, None to use parser's default. Returns: A config object with all parsed values. @@ -582,7 +566,6 @@ def parse_env( kwargs = { "env": True, "defaults": defaults, - "with_meta": with_meta, "skip_validation": skip_validation, "skip_subcommands": skip_subcommands, } @@ -603,7 +586,6 @@ def parse_path( ext_vars: Optional[dict] = None, env: Optional[bool] = None, defaults: bool = True, - with_meta: Optional[bool] = None, **kwargs, ) -> Namespace: """Parses a configuration file given its path. @@ -613,7 +595,6 @@ def parse_path( ext_vars: Optional external variables used for parsing jsonnet. env: Whether to merge with the parsed environment, None to use parser's default. defaults: Whether to merge with the parser's defaults. - with_meta: Whether to include metadata in config object, None to use parser's default. Returns: A config object with all parsed values. @@ -625,12 +606,11 @@ def parse_path( with change_to_path_dir(fpath): cfg_str = fpath.get_content() parsed_cfg = self.parse_string( - cfg_str, - os.path.basename(cfg_path), - ext_vars, - env, - defaults, - with_meta, + cfg_str=cfg_str, + cfg_path=os.path.basename(cfg_path), + ext_vars=ext_vars, + env=env, + defaults=defaults, **kwargs, ) @@ -644,7 +624,6 @@ def parse_string( ext_vars: Optional[dict] = None, env: Optional[bool] = None, defaults: bool = True, - with_meta: Optional[bool] = None, **kwargs, ) -> Namespace: """Parses configuration given as a string. @@ -655,7 +634,6 @@ def parse_string( ext_vars: Optional external variables used for parsing jsonnet. env: Whether to merge with the parsed environment, None to use parser's default. defaults: Whether to merge with the parser's defaults. - with_meta: Whether to include metadata in config object, None to use parser's default. Returns: A config object with all parsed values. @@ -679,7 +657,6 @@ def parse_string( cfg=cfg, env=env, defaults=defaults, - with_meta=with_meta, skip_validation=skip_validation, fail_no_subcommand=fail_no_subcommand, ) @@ -1051,7 +1028,6 @@ def get_defaults(self, skip_validation: bool = False, **kwargs) -> Namespace: cfg=cfg, env=False, defaults=False, - with_meta=None, skip_validation=skip_validation, skip_required=True, ) @@ -1555,25 +1531,6 @@ def default_env(self, default_env: bool): for subparser in self._subcommands_action._name_parser_map.values(): subparser.default_env = self._default_env - @property - def default_meta(self) -> bool: - """Whether by default metadata is included in config objects. - - :getter: Returns the current default metadata setting. - :setter: Sets the default metadata setting. - - Raises: - ValueError: If an invalid value is given. - """ - return self._default_meta - - @default_meta.setter - def default_meta(self, default_meta: bool): - if isinstance(default_meta, bool): - self._default_meta = default_meta - else: - raise ValueError("default_meta expects a boolean.") - @property def env_prefix(self) -> Union[bool, str]: """The environment variables prefix property. diff --git a/jsonargparse/_deprecated.py b/jsonargparse/_deprecated.py index 37e56a2a..9e837dc2 100644 --- a/jsonargparse/_deprecated.py +++ b/jsonargparse/_deprecated.py @@ -107,19 +107,24 @@ def parse_as_dict_patch(): assert not hasattr(ArgumentParser, "_unpatched_init") - message = """ + message_parse_as_dict = """ ``parse_as_dict`` parameter was deprecated in v4.0.0 and will be removed in v5.0.0. After removal, the parse_*, dump, save and instantiate_classes methods will only return Namespace and/or accept Namespace objects. If needed for some use case, config objects can be converted to a nested dict using the Namespace.as_dict method. """ + message_with_meta = """ + ``with_meta`` parameter was deprecated in v4.44.0 and will be removed in + v5.0.0. After removal, config objects will always include metadata. To + remove metadata from a config object, do ``.clone(with_meta=False)``. + """ # Patch __init__ def patched_init(self, *args, parse_as_dict: bool = False, **kwargs): self._parse_as_dict = parse_as_dict if parse_as_dict: - deprecation_warning(patched_init, message) + deprecation_warning(patched_init, message_parse_as_dict) self._unpatched_init(*args, **kwargs) ArgumentParser._unpatched_init = ArgumentParser.__init__ @@ -129,9 +134,21 @@ def patched_init(self, *args, parse_as_dict: bool = False, **kwargs): def patch_parse_method(method_name): unpatched_method_name = "_unpatched_" + method_name - def patched_parse(self, *args, _skip_validation: bool = False, **kwargs) -> Union[Namespace, Dict[str, Any]]: + def patched_parse( + self, + *args, + with_meta: Optional[bool] = None, + _skip_validation: bool = False, + **kwargs, + ) -> Union[Namespace, Dict[str, Any]]: parse_method = getattr(self, unpatched_method_name) cfg = parse_method(*args, _skip_validation=_skip_validation, **kwargs) + + if isinstance(with_meta, bool): + deprecation_warning(patched_parse, message_with_meta) + if not (with_meta or (with_meta is None and self._default_meta)): + cfg = cfg.clone(with_meta=False) + return cfg.as_dict() if self._parse_as_dict and not _skip_validation else cfg setattr(ArgumentParser, unpatched_method_name, getattr(ArgumentParser, method_name)) @@ -539,12 +556,23 @@ def deprecation_warning_error_handler(stacklevel): deprecation_warning("ArgumentParser.error_handler", error_handler_message, stacklevel=stacklevel) +default_meta_message = """ + ``default_meta`` property was deprecated in v4.44.0 and will be removed in + v5.0.0. After removal, config objects will always include metadata. To + remove metadata from a config object, do ``.clone(with_meta=False)``. +""" + + class ParserDeprecations: """Helper class for ArgumentParser deprecations. Will be removed in v5.0.0.""" - def __init__(self, *args, error_handler=False, **kwargs): + def __init__(self, *args, error_handler=False, default_meta=None, **kwargs): super().__init__(*args, **kwargs) self.error_handler = error_handler + if default_meta is None: + self._default_meta = True + else: + self.default_meta = default_meta @property @deprecated("error_handler property is deprecated and will be removed in v5.0.0.") @@ -572,6 +600,27 @@ def error_handler(self, error_handler): else: raise ValueError("error_handler can be either a Callable or None.") + @property + @deprecated(default_meta_message) + def default_meta(self) -> bool: + """Whether by default metadata is included in config objects. + + :getter: Returns the current default metadata setting. + :setter: Sets the default metadata setting. + + Raises: + ValueError: If an invalid value is given. + """ + return self._default_meta + + @default_meta.setter + def default_meta(self, default_meta: bool): + if isinstance(default_meta, bool): + deprecation_warning("ArgumentParser.default_meta", default_meta_message) + self._default_meta = default_meta + else: + raise ValueError("default_meta expects a boolean.") + @deprecated( """ instantiate_subclasses was deprecated in v4.0.0 and will be removed in v5.0.0. diff --git a/jsonargparse_tests/test_actions.py b/jsonargparse_tests/test_actions.py index d4ddca40..615c6bf9 100644 --- a/jsonargparse_tests/test_actions.py +++ b/jsonargparse_tests/test_actions.py @@ -241,7 +241,7 @@ def test_action_parser_parse_path(composed_parsers): yaml_main2 = yaml_main.parent / "main2.yaml" yaml_main2.write_text(parser.dump(cfg)) - cfg2 = parser.parse_path(yaml_main2, with_meta=False) + cfg2 = parser.parse_path(yaml_main2).clone(with_meta=False) assert expected == cfg2.as_dict() @@ -250,7 +250,7 @@ def test_action_parser_parse_env_inner(composed_parsers): assert "opt2_env" == parser.parse_env({"LV1_INNER2__OPT2": "opt2_env"}).inner2.opt2 assert "opt3_env" == parser.parse_env({"LV1_INNER2__INNER3__OPT3": "opt3_env"}).inner2.inner3.opt3 expected = {"opt1": "opt1_def", "inner2": {"opt2": "opt2_def", "inner3": {"opt3": "opt3_yaml"}}} - cfg = parser.parse_env({"LV1_INNER2__INNER3": str(yaml_inner3)}, with_meta=False) + cfg = parser.parse_env({"LV1_INNER2__INNER3": str(yaml_inner3)}).clone(with_meta=False) assert expected == cfg.as_dict() assert "opt2_yaml" == parser.parse_env({"LV1_INNER2": str(yaml_inner2)}).inner2.opt2 @@ -259,15 +259,17 @@ def test_action_parser_parse_args_subconfig_path(composed_parsers): parser, _, yaml_inner2, yaml_inner3 = composed_parsers expected = {"opt1": "opt1_arg", "inner2": {"opt2": "opt2_yaml", "inner3": {"opt3": "opt3_yaml"}}} - cfg = parser.parse_args(["--opt1", "opt1_arg", f"--inner2={yaml_inner2}"], with_meta=False) + cfg = parser.parse_args(["--opt1", "opt1_arg", f"--inner2={yaml_inner2}"]).clone(with_meta=False) assert expected == cfg.as_dict() expected = {"opt1": "opt1_def", "inner2": {"opt2": "opt2_arg", "inner3": {"opt3": "opt3_yaml"}}} - cfg = parser.parse_args(["--inner2.opt2", "opt2_arg", f"--inner2.inner3={yaml_inner3}"], with_meta=False) + cfg = parser.parse_args(["--inner2.opt2", "opt2_arg", f"--inner2.inner3={yaml_inner3}"]).clone(with_meta=False) assert expected == cfg.as_dict() expected = {"opt1": "opt1_def", "inner2": {"opt2": "opt2_def", "inner3": {"opt3": "opt3_arg"}}} - cfg = parser.parse_args([f"--inner2.inner3={yaml_inner3}", "--inner2.inner3.opt3", "opt3_arg"], with_meta=False) + cfg = parser.parse_args([f"--inner2.inner3={yaml_inner3}", "--inner2.inner3.opt3", "opt3_arg"]).clone( + with_meta=False + ) assert expected == cfg.as_dict() @@ -275,11 +277,11 @@ def test_action_parser_parse_args_subconfig_string(composed_parsers): parser = composed_parsers[0] expected = {"opt2": "opt2_str", "inner3": {"opt3": "opt3_str"}} - cfg = parser.parse_args([f"--inner2={json_or_yaml_dump(expected)}"], with_meta=False) + cfg = parser.parse_args([f"--inner2={json_or_yaml_dump(expected)}"]).clone(with_meta=False) assert expected == cfg.inner2.as_dict() expected = {"opt3": "opt3_str"} - cfg = parser.parse_args([f"--inner2.inner3={json_or_yaml_dump(expected)}"], with_meta=False) + cfg = parser.parse_args([f"--inner2.inner3={json_or_yaml_dump(expected)}"]).clone(with_meta=False) assert expected == cfg.inner2.inner3.as_dict() @@ -288,7 +290,7 @@ def test_action_parser_parse_args_global_config(composed_parsers): parser.add_argument("--cfg", action="config") expected = {"opt1": "opt1_yaml", "inner2": {"opt2": "opt2_yaml", "inner3": {"opt3": "opt3_yaml"}}} - cfg = parser.parse_args([f"--cfg={yaml_main}"], with_meta=False) + cfg = parser.parse_args([f"--cfg={yaml_main}"]).clone(with_meta=False) delattr(cfg, "cfg") assert expected == cfg.as_dict() diff --git a/jsonargparse_tests/test_core.py b/jsonargparse_tests/test_core.py index 0e264e76..639ef471 100644 --- a/jsonargparse_tests/test_core.py +++ b/jsonargparse_tests/test_core.py @@ -621,7 +621,7 @@ def test_parse_args_url_config(parser_schema_jsonnet): responses.add(responses.GET, base_url + name, status=200, body=body) responses.add(responses.HEAD, base_url + name, status=200) - cfg = parser.parse_args([f"--cfg={base_url}main.yaml"], with_meta=False) + cfg = parser.parse_args([f"--cfg={base_url}main.yaml"]).clone(with_meta=False) assert expected.subparser == cfg.subparser if jsonschema_support: assert expected.schema == cfg.schema @@ -665,7 +665,7 @@ def rm_out_files(): file.unlink() with subtests.test("parse_path with metadata"): - cfg1 = parser.parse_path(main_file_in, with_meta=True) + cfg1 = parser.parse_path(main_file_in).clone(with_meta=True) assert expected == cfg1.clone(with_meta=False) assert str(cfg1.subparser["__path__"]) == "subparser.yaml" if jsonschema_support: @@ -680,13 +680,13 @@ def rm_out_files(): assert schema_file_out.is_file() if jsonnet_support: assert jsonnet_file_out.read_text() == '{"c": 3, "d": 4}' - cfg2 = parser.parse_path(main_file_out, with_meta=False) + cfg2 = parser.parse_path(main_file_out).clone(with_meta=False) assert expected == cfg2 with subtests.test("save without metadata (single-file)"): rm_out_files() parser.save(cfg1, main_file_out, multifile=False) - cfg3 = parser.parse_path(main_file_out, with_meta=False) + cfg3 = parser.parse_path(main_file_out).clone(with_meta=False) assert expected == cfg3 if jsonschema_support: @@ -1130,20 +1130,6 @@ def test_parse_known_args_not_implemented_without_caller_module(parser): pytest.raises(NotImplementedError, lambda: parser.parse_known_args([])) -def test_default_meta_property(): - parser = ArgumentParser() - assert True is parser.default_meta - parser.default_meta = False - assert False is parser.default_meta - parser = ArgumentParser(default_meta=False) - assert False is parser.default_meta - parser.default_meta = True - assert True is parser.default_meta - with pytest.raises(ValueError) as ctx: - parser.default_meta = "invalid" - ctx.match("default_meta expects a boolean") - - def test_pickle_parser(example_parser): parser = pickle.loads(pickle.dumps(example_parser)) assert example_parser.get_defaults() == parser.get_defaults() diff --git a/jsonargparse_tests/test_deprecated.py b/jsonargparse_tests/test_deprecated.py index 400a7baf..1184552e 100644 --- a/jsonargparse_tests/test_deprecated.py +++ b/jsonargparse_tests/test_deprecated.py @@ -128,7 +128,7 @@ def test_ActionEnum(): for val in ["X", "b", 2]: pytest.raises(ArgumentError, lambda: parser.parse_args(["--enum=" + str(val)])) - cfg = parser.parse_args(["--enum=C"], with_meta=False) + cfg = parser.parse_args(["--enum=C"]).clone(with_meta=False) assert "enum: C\n" == parser.dump(cfg) help_str = get_parser_help(parser) @@ -398,11 +398,11 @@ def test_parse_as_dict(tmp_cwd): with open("config.json", "w") as f: f.write("{}") with catch_warnings(record=True) as w: - parser = ArgumentParser(parse_as_dict=True, default_meta=False) + parser = ArgumentParser(parse_as_dict=True) assert_deprecation_warn( w, message="``parse_as_dict`` parameter was deprecated", - code="ArgumentParser(parse_as_dict=True,", + code="ArgumentParser(parse_as_dict=True)", ) assert {} == parser.parse_args([]) assert {} == parser.parse_env([]) @@ -416,6 +416,41 @@ def test_parse_as_dict(tmp_cwd): assert "{}\n" == f.read() +def test_default_meta_property(parser): + with catch_warnings(record=True) as w: + assert True is parser.default_meta + assert_deprecation_warn( + w, + message="``default_meta`` property was deprecated", + code="True is parser.default_meta", + ) + with catch_warnings(record=True) as w: + parser.default_meta = False + assert_deprecation_warn( + w, + message="``default_meta`` property was deprecated", + code="parser.default_meta = False", + ) + assert False is parser.default_meta + parser = ArgumentParser(default_meta=False) + assert False is parser.default_meta + parser.default_meta = True + assert True is parser.default_meta + with pytest.raises(ValueError) as ctx: + parser.default_meta = "invalid" + ctx.match("default_meta expects a boolean") + + +def test_parse_with_meta_parameter(parser): + with catch_warnings(record=True) as w: + parser.parse_args([], with_meta=False) + assert_deprecation_warn( + w, + message="``with_meta`` parameter was deprecated in v4.44.0 and will be removed in v5.0.0", + code="parser.parse_args([], with_meta=False)", + ) + + def test_deprecated_skip_check_method(parser): parser.add_argument("--key", type=int) cfg = Namespace(key=1) diff --git a/jsonargparse_tests/test_signatures.py b/jsonargparse_tests/test_signatures.py index 656afd5f..83e7e04b 100644 --- a/jsonargparse_tests/test_signatures.py +++ b/jsonargparse_tests/test_signatures.py @@ -101,7 +101,7 @@ def test_add_class_without_nesting(parser): for key in ["c2_a0", "c1_a1", "c0_a0"]: assert _find_action(parser, key) is None, f"{key} should not be in parser but is" - cfg = parser.parse_args(["--c3_a0=0", "--c3_a3=true", "--c3_a4=a"], with_meta=False) + cfg = parser.parse_args(["--c3_a0=0", "--c3_a3=true", "--c3_a4=a"]).clone(with_meta=False) assert cfg.as_dict() == { "c1_a2": 2.0, "c1_a3": None, @@ -512,7 +512,7 @@ def test_add_method_normal_and_static(parser): assert _find_action(parser, key) is not None, f"{key} should be in parser but is not" assert _find_action(parser, "s._a3") is None, "s._a3 should not be in parser but is" - cfg = parser.parse_args(["--m.a1=x", "--s.a1=y"], with_meta=False).as_dict() + cfg = parser.parse_args(["--m.a1=x", "--s.a1=y"]).clone(with_meta=False).as_dict() assert cfg == {"m": {"a1": "x", "a2": 2.0, "a3": False}, "s": {"a1": "y", "a2": 2.0}} assert "x" == WithMethods().normal_method(**cfg["m"]) assert "y" == WithMethods.static_method(**cfg["s"]) @@ -566,7 +566,7 @@ def test_add_function_arguments(parser): for key in ["a1", "a2", "a3", "a4"]: assert _find_action(parser, key) is not None, f"{key} should be in parser but is not" - cfg = parser.parse_args(["--a1=x"], with_meta=False).as_dict() + cfg = parser.parse_args(["--a1=x"]).clone(with_meta=False).as_dict() assert cfg == {"a1": "x", "a2": 2.0, "a3": False, "a4": None} assert "x" == func(**cfg) @@ -655,16 +655,15 @@ def func_config(a1="1", a2: float = 2.0, a3: bool = False): def test_add_function_group_config(parser, tmp_cwd): - parser.default_meta = False parser.add_function_arguments(func, "func") cfg_path = Path("config.yaml") cfg_path.write_text(json_or_yaml_dump({"a1": "one", "a3": True})) - cfg = parser.parse_args([f"--func={cfg_path}"]) + cfg = parser.parse_args([f"--func={cfg_path}"]).clone(with_meta=False) assert cfg.func == Namespace(a1="one", a2=2.0, a3=True, a4=None) - cfg = parser.parse_args(['--func={"a1": "ONE"}']) + cfg = parser.parse_args(['--func={"a1": "ONE"}']).clone(with_meta=False) assert cfg.func == Namespace(a1="ONE", a2=2.0, a3=False, a4=None) with pytest.raises(ArgumentError) as ctx: diff --git a/jsonargparse_tests/test_subcommands.py b/jsonargparse_tests/test_subcommands.py index 9df470f1..b81e03bb 100644 --- a/jsonargparse_tests/test_subcommands.py +++ b/jsonargparse_tests/test_subcommands.py @@ -252,9 +252,9 @@ def test_subcommand_default_config_repeated_keys(parser, subparser, tmp_cwd): subcommands = parser.add_subcommands() subcommands.add_subcommand("test", subparser) - cfg = parser.parse_args([], with_meta=False) + cfg = parser.parse_args([]).clone(with_meta=False) assert cfg == Namespace(subcommand="test", test=Namespace(test="value")) - cfg = parser.parse_args(["test", "--test=x"], with_meta=False) + cfg = parser.parse_args(["test", "--test=x"]).clone(with_meta=False) assert cfg == Namespace(subcommand="test", test=Namespace(test="x")) @@ -269,9 +269,9 @@ def test_subsubcommand_default_config_repeated_keys(parser, subparser, tmp_cwd): subcommands2 = subparser.add_subcommands() subcommands2.add_subcommand("test", subsubparser) - cfg = parser.parse_args([], with_meta=False) + cfg = parser.parse_args([]).clone(with_meta=False) assert cfg.as_dict() == {"subcommand": "test", "test": {"subcommand": "test", "test": {"test": "value"}}} - cfg = parser.parse_args(["test", "test", "--test=x"], with_meta=False) + cfg = parser.parse_args(["test", "test", "--test=x"]).clone(with_meta=False) assert cfg.as_dict() == {"subcommand": "test", "test": {"subcommand": "test", "test": {"test": "x"}}} @@ -336,7 +336,7 @@ def test_subsubcommands_parse_args(subtests): parser_s2_b = ArgumentParser(exit_on_error=False) parser_s2_b.add_argument("--os2b", default="os2b_def") - parser = ArgumentParser(prog="app", exit_on_error=False, default_meta=False) + parser = ArgumentParser(prog="app", exit_on_error=False) subcommands1 = parser.add_subcommands() subcommands1.add_subcommand("a", parser_s1_a)