From a1e2c11d399572dd6ca0fa8016fe27dac038e037 Mon Sep 17 00:00:00 2001 From: joncrall Date: Sat, 18 Mar 2023 19:23:07 -0400 Subject: [PATCH 1/6] POC to allow alias --- jsonargparse/actions.py | 13 ++++++++++++- jsonargparse/core.py | 20 +++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/jsonargparse/actions.py b/jsonargparse/actions.py index 9cdf6ec2..1b98374e 100644 --- a/jsonargparse/actions.py +++ b/jsonargparse/actions.py @@ -69,8 +69,11 @@ def _find_action_and_subcommand( if exclude is not None: actions = [a for a in actions if not isinstance(a, exclude)] fallback_action = None + + ALLOW_ALIAS = True + for action in actions: - if action.dest == dest: + if action.dest == dest or (ALLOW_ALIAS and dest in get_alias_dest(action)): if isinstance(action, _ActionConfigLoad): fallback_action = action else: @@ -746,3 +749,11 @@ def handle_subcommands( # Handle inner subcommands if subparser._subparsers is not None: _ActionSubCommands.handle_subcommands(subparser, cfg, env, defaults, key+'.', fail_no_subcommand=fail_no_subcommand) + + +def get_alias_dest(action): + def option_string_to_var(optstr): + " normalize a cli key as a variable " + return optstr.lstrip('-').replace('-', '_') + + return [option_string_to_var(optstr) for optstr in action.option_strings] diff --git a/jsonargparse/core.py b/jsonargparse/core.py index 48333828..2d4796fa 100644 --- a/jsonargparse/core.py +++ b/jsonargparse/core.py @@ -1254,7 +1254,25 @@ def _apply_actions( continue action_dest = action.dest if subcommand is None else subcommand+'.'+action.dest - value = cfg[action_dest] + ALLOW_ALIAS = 1 + try: + value = cfg[action_dest] + except KeyError: + from jsonargparse.actions import get_alias_dest + if ALLOW_ALIAS: + # If the main key isn't in the config, check if it exists + # under an alias. + found = None + for alias in get_alias_dest(action): + if alias in cfg: + value = cfg[alias] + found = True + break + if not found: + raise + else: + raise + ... if skip_fn and skip_fn(value): continue with parser_context(parent_parser=self, lenient_check=True): From f835e944fb1ed48965827e4b8371f23e8a1d4997 Mon Sep 17 00:00:00 2001 From: joncrall Date: Sat, 18 Mar 2023 19:57:02 -0400 Subject: [PATCH 2/6] Add test --- jsonargparse_tests/test_alias.py | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 jsonargparse_tests/test_alias.py diff --git a/jsonargparse_tests/test_alias.py b/jsonargparse_tests/test_alias.py new file mode 100644 index 00000000..a4ddb144 --- /dev/null +++ b/jsonargparse_tests/test_alias.py @@ -0,0 +1,10 @@ +def test_alias(): + import jsonargparse + parser = jsonargparse.ArgumentParser() + parser.add_argument('--foo', '--bar') + parsed = parser.parse_string('foo: "aaa"') + assert parsed.foo == 'aaa' + parsed = parser.parse_string('bar: "bbb"') + assert parsed.foo == 'bbb' + parsed = parser.parse_args(['--bar', 'ccc']) + assert parsed.foo == 'ccc' From af8b18503d14533564894b3eb1f88b3ed78dd337 Mon Sep 17 00:00:00 2001 From: joncrall Date: Mon, 24 Apr 2023 10:33:27 -0400 Subject: [PATCH 3/6] Move alias test to core --- jsonargparse_tests/test_alias.py | 10 ---------- jsonargparse_tests/test_core.py | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 10 deletions(-) delete mode 100644 jsonargparse_tests/test_alias.py diff --git a/jsonargparse_tests/test_alias.py b/jsonargparse_tests/test_alias.py deleted file mode 100644 index a4ddb144..00000000 --- a/jsonargparse_tests/test_alias.py +++ /dev/null @@ -1,10 +0,0 @@ -def test_alias(): - import jsonargparse - parser = jsonargparse.ArgumentParser() - parser.add_argument('--foo', '--bar') - parsed = parser.parse_string('foo: "aaa"') - assert parsed.foo == 'aaa' - parsed = parser.parse_string('bar: "bbb"') - assert parsed.foo == 'bbb' - parsed = parser.parse_args(['--bar', 'ccc']) - assert parsed.foo == 'ccc' diff --git a/jsonargparse_tests/test_core.py b/jsonargparse_tests/test_core.py index b76dfe14..e4c8fc61 100755 --- a/jsonargparse_tests/test_core.py +++ b/jsonargparse_tests/test_core.py @@ -1223,5 +1223,22 @@ def test_add_multiple_config_arguments_error(self): parser.add_argument('--cfg2', action=ActionConfigFile) + def test_parser_alias(self): + import jsonargparse + + # Create a parser where --bar is an alias for --foo + parser = jsonargparse.ArgumentParser() + parser.add_argument('--foo', '--bar') + + parsed = parser.parse_string('foo: "aaa"') + assert parsed.foo == 'aaa' + + parsed = parser.parse_string('bar: "bbb"') + assert parsed.foo == 'bbb' + + parsed = parser.parse_args(['--bar', 'ccc']) + assert parsed.foo == 'ccc' + + if __name__ == '__main__': unittest.main(verbosity=2) From a1f86ec2212a1c31dafd5a10efd377846f4c685f Mon Sep 17 00:00:00 2001 From: joncrall Date: Mon, 24 Apr 2023 10:57:04 -0400 Subject: [PATCH 4/6] Cache aliases function / stub aliases property --- jsonargparse/actions.py | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/jsonargparse/actions.py b/jsonargparse/actions.py index 1b98374e..f51c1a30 100644 --- a/jsonargparse/actions.py +++ b/jsonargparse/actions.py @@ -39,6 +39,19 @@ class Action(LoggerProperty, ArgparseAction): """Base for jsonargparse Action classes.""" + @property + def aliases(self): + return _action_aliases(self) + + +def _action_aliases(self): + if not hasattr(self, '_aliases'): + options = {optstr.lstrip('-').replace('-', '_') + for optstr in self.option_strings} + options = {opt for opt in options if len(opt) > 1} + self._aliases = {self.dest} | options + return self._aliases + def _is_branch_key(parser, key: str) -> bool: root_key = split_key_root(key)[0] @@ -70,10 +83,10 @@ def _find_action_and_subcommand( actions = [a for a in actions if not isinstance(a, exclude)] fallback_action = None - ALLOW_ALIAS = True - for action in actions: - if action.dest == dest or (ALLOW_ALIAS and dest in get_alias_dest(action)): + # _StoreAction seems to break the property + # if dest in action.aliases: + if dest in _action_aliases(action): if isinstance(action, _ActionConfigLoad): fallback_action = action else: @@ -752,8 +765,5 @@ def handle_subcommands( def get_alias_dest(action): - def option_string_to_var(optstr): - " normalize a cli key as a variable " - return optstr.lstrip('-').replace('-', '_') - - return [option_string_to_var(optstr) for optstr in action.option_strings] + return [optstr.lstrip('-').replace('-', '_') + for optstr in action.option_strings] From 5ba2364566ead63ca4b9ebe4dff35d97f1fc2122 Mon Sep 17 00:00:00 2001 From: joncrall Date: Mon, 24 Apr 2023 11:04:55 -0400 Subject: [PATCH 5/6] Remove old get_aliases function --- jsonargparse/actions.py | 5 ----- jsonargparse/core.py | 26 +++++++++++--------------- jsonargparse_tests/test_core.py | 8 ++++++-- 3 files changed, 17 insertions(+), 22 deletions(-) diff --git a/jsonargparse/actions.py b/jsonargparse/actions.py index f51c1a30..b352d2cc 100644 --- a/jsonargparse/actions.py +++ b/jsonargparse/actions.py @@ -762,8 +762,3 @@ def handle_subcommands( # Handle inner subcommands if subparser._subparsers is not None: _ActionSubCommands.handle_subcommands(subparser, cfg, env, defaults, key+'.', fail_no_subcommand=fail_no_subcommand) - - -def get_alias_dest(action): - return [optstr.lstrip('-').replace('-', '_') - for optstr in action.option_strings] diff --git a/jsonargparse/core.py b/jsonargparse/core.py index 2d4796fa..523a7fd5 100644 --- a/jsonargparse/core.py +++ b/jsonargparse/core.py @@ -1254,25 +1254,21 @@ def _apply_actions( continue action_dest = action.dest if subcommand is None else subcommand+'.'+action.dest - ALLOW_ALIAS = 1 try: value = cfg[action_dest] except KeyError: - from jsonargparse.actions import get_alias_dest - if ALLOW_ALIAS: - # If the main key isn't in the config, check if it exists - # under an alias. - found = None - for alias in get_alias_dest(action): - if alias in cfg: - value = cfg[alias] - found = True - break - if not found: - raise - else: + from jsonargparse.actions import _action_aliases + # If the main key isn't in the config, check if it exists + # under an alias. + found = None + # for alias in get_alias_dest(action): + for alias in _action_aliases(action): + if alias in cfg: + value = cfg[alias] + found = True + break + if not found: raise - ... if skip_fn and skip_fn(value): continue with parser_context(parent_parser=self, lenient_check=True): diff --git a/jsonargparse_tests/test_core.py b/jsonargparse_tests/test_core.py index e4c8fc61..608a20f7 100755 --- a/jsonargparse_tests/test_core.py +++ b/jsonargparse_tests/test_core.py @@ -1224,10 +1224,14 @@ def test_add_multiple_config_arguments_error(self): def test_parser_alias(self): - import jsonargparse + # TODO: + # Successful parsing of a value in an alias saved as the dest key and the alias not appearing in the namespace. + # Failure to parse alias because the value does not agree with the argument type. + # Alias in a nested namespace. + # Alias with name that includes dashes -. # Create a parser where --bar is an alias for --foo - parser = jsonargparse.ArgumentParser() + parser = ArgumentParser() parser.add_argument('--foo', '--bar') parsed = parser.parse_string('foo: "aaa"') From fc6266f9e0cafb90dd9b583bc80fe098d484f733 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:06:50 +0000 Subject: [PATCH 6/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- jsonargparse/core.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jsonargparse/core.py b/jsonargparse/core.py index 523a7fd5..523286ad 100644 --- a/jsonargparse/core.py +++ b/jsonargparse/core.py @@ -1258,6 +1258,7 @@ def _apply_actions( value = cfg[action_dest] except KeyError: from jsonargparse.actions import _action_aliases + # If the main key isn't in the config, check if it exists # under an alias. found = None