diff --git a/borgmatic/commands/arguments.py b/borgmatic/commands/arguments.py index 3a527799..b68590b8 100644 --- a/borgmatic/commands/arguments.py +++ b/borgmatic/commands/arguments.py @@ -28,6 +28,37 @@ SUBPARSER_ALIASES = { } +def parse_subparser_arguments(arguments, unparsed_arguments, subparsers): + remaining_subparser_arguments = [] + + for subparser_name, subparser in reversed(subparsers.items()): + if subparser_name not in arguments.keys(): + continue + + subparser = subparsers[subparser_name] + unused_parsed, remaining = subparser.parse_known_args( + [argument for argument in unparsed_arguments if argument != subparser_name] + ) + remaining_subparser_arguments.append(remaining) + + # Determine the remaining arguments that no subparsers have consumed. + if remaining_subparser_arguments: + remaining_arguments = [ + argument + for argument in dict.fromkeys( + itertools.chain.from_iterable(remaining_subparser_arguments) + ).keys() + if all( + argument in subparser_arguments + for subparser_arguments in remaining_subparser_arguments + ) + ] + else: + remaining_arguments = [] + + return remaining_arguments + + def parse_subparser_arguments(unparsed_arguments, subparsers): ''' Given a sequence of arguments and a dict from subparser name to argparse.ArgumentParser @@ -97,30 +128,7 @@ def parse_subparser_arguments(unparsed_arguments, subparsers): # Now ask each subparser, one by one, to greedily consume arguments, from last to first. This # allows subparsers to consume arguments before their parent subparsers do. - remaining_subparser_arguments = [] - - for subparser_name, subparser in reversed(subparsers.items()): - if subparser_name not in arguments.keys(): - continue - - subparser = subparsers[subparser_name] - unused_parsed, remaining = subparser.parse_known_args( - [argument for argument in unparsed_arguments if argument != subparser_name] - ) - remaining_subparser_arguments.append(remaining) - - # Determine the remaining arguments that no subparsers have consumed. - if remaining_subparser_arguments: - remaining_arguments = [ - argument - for argument in dict.fromkeys( - itertools.chain.from_iterable(remaining_subparser_arguments) - ).keys() - if all( - argument in subparser_arguments - for subparser_arguments in remaining_subparser_arguments - ) - ] + remaining_arguments = parse_subparser_arguments(arguments, unparsed_arguments, subparsers) # Special case: If "borg" is present in the arguments, consume all arguments after (+1) the # "borg" action. @@ -973,11 +981,9 @@ def make_parsers(): for name, subparser in subparsers.choices.items(): merged_subparsers._name_parser_map[name] = subparser - subparser._name_parser_map = merged_subparsers._name_parser_map for name, subparser in config_subparsers.choices.items(): merged_subparsers._name_parser_map[name] = subparser - subparser._name_parser_map = merged_subparsers._name_parser_map return top_level_parser, merged_subparsers diff --git a/tests/unit/commands/test_arguments.py b/tests/unit/commands/test_arguments.py index fafec390..c532022e 100644 --- a/tests/unit/commands/test_arguments.py +++ b/tests/unit/commands/test_arguments.py @@ -175,3 +175,22 @@ def test_parse_subparser_arguments_raises_error_when_no_subparser_is_specified() with pytest.raises(ValueError): module.parse_subparser_arguments(('config',), subparsers) + + +@pytest.mark.parametrize( + 'arguments, unparsed_arguments, subparsers, expected_remaining_arguments', + [ + ( + {'action': flexmock()}, + ['--verbosity', 'lots'], + {'action': flexmock(parse_known_args=lambda arguments: (flexmock(), ['--verbosity', 'lots']))}, + ['--verbosity', 'lots'], + ), + ], +) +def test_get_remaining_arguments_returns_expected_remaining_arguments( + arguments, unparsed_arguments, subparsers, expected_remaining_arguments +): + remaining_arguments = module.get_remaining_arguments(arguments, unparsed_arguments, subparsers) + + assert remaining_arguments == expected_remaining_arguments \ No newline at end of file