From 6f06877ed43c3762d9e4eef97a50a71e79dfb424 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Wed, 24 Jul 2024 13:27:10 -0500 Subject: [PATCH 1/4] Add support for command invocation with a 'default verb' There are no plans to use this feature to allow the 'colcon' executable to be invoked without a verb, but other tools built on colcon-core's framework may not want multiple verbs. --- colcon_core/command.py | 27 ++++++++++++++++++++++----- test/test_command.py | 24 ++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 5 deletions(-) diff --git a/colcon_core/command.py b/colcon_core/command.py index 9c94118c..6d8565a5 100644 --- a/colcon_core/command.py +++ b/colcon_core/command.py @@ -93,7 +93,7 @@ def register_command_exit_handler(handler): def main( *, command_name='colcon', argv=None, verb_group_name=None, - environment_variable_group_name=None, + environment_variable_group_name=None, default_verb=None, ): """ Execute the main logic of the command. @@ -114,13 +114,19 @@ def main( :param str command_name: The name of the command invoked :param list argv: The list of arguments + :param str verb_group_name: The extension point group name for verbs + :param str environment_variable_group_name: The extension point group name + for environment variables + :param VerbExtensionPoint default_verb: The verb to invoke if no explicit + verb was provided on the command line :returns: The return code """ try: return _main( command_name=command_name, argv=argv, verb_group_name=verb_group_name, - environment_variable_group_name=environment_variable_group_name) + environment_variable_group_name=environment_variable_group_name, + default_verb=default_verb) except KeyboardInterrupt: return signal.SIGINT finally: @@ -132,6 +138,7 @@ def main( def _main( *, command_name, argv, verb_group_name, environment_variable_group_name, + default_verb ): # default log level, for searchability: COLCON_LOG_LEVEL colcon_logger.setLevel(logging.WARNING) @@ -151,6 +158,12 @@ def _main( parser = create_parser(environment_variable_group_name) + if default_verb is not None: + parser.set_defaults( + verb_parser=parser, verb_extension=default_verb, + main=default_verb.main) + add_parser_arguments(parser, default_verb) + verb_extensions = get_verb_extensions(group_name=verb_group_name) # add subparsers for all verb extensions but without arguments for now @@ -163,7 +176,7 @@ def _main( known_args, _ = parser.parse_known_args(args=argv) # add the arguments for the requested verb - if known_args.verb_name: + if known_args.verb_name is not None: add_parser_arguments(known_args.verb_parser, known_args.verb_extension) args = parser.parse_args(args=argv) @@ -176,17 +189,21 @@ def _main( colcon_logger.debug(f'Parsed command line arguments: {args}') # error: no verb provided - if args.verb_name is None: + if args.verb_name is None and default_verb is None: print(parser.format_usage()) return 'Error: No verb provided' # set default locations for log files, for searchability: COLCON_LOG_PATH now = datetime.datetime.now() now_str = str(now)[:-7].replace(' ', '_').replace(':', '-') + if args.verb_name is None: + subdirectory = now_str + else: + subdirectory = f'{args.verb_name}_{now_str}' set_default_log_path( base_path=args.log_base, env_var=f'{command_name}_LOG_PATH'.upper(), - subdirectory=f'{args.verb_name}_{now_str}') + subdirectory=subdirectory) # add a file handler writing all levels if logging isn't disabled log_path = get_log_path() diff --git a/test/test_command.py b/test/test_command.py index c8c6b6e9..b03616a4 100644 --- a/test/test_command.py +++ b/test/test_command.py @@ -96,6 +96,30 @@ def test_main_no_verbs_or_env(): assert e.value.code == 0 +def test_main_default_verb(): + default_verb = Extension2() + default_verb.main = Mock(wraps=default_verb.main) + with ExtensionPointContext(): + with patch( + 'colcon_core.argument_parser.get_argument_parser_extensions', + return_value={} + ): + with pytest.raises(SystemExit) as e: + main(argv=['--help'], default_verb=default_verb) + assert e.value.code == 0 + + with pytest.raises(SystemExit) as e: + main( + argv=['--log-level', 'invalid'], + default_verb=default_verb) + assert e.value.code == 2 + + assert not main( + argv=['--log-base', '/dev/null'], + default_verb=default_verb) + default_verb.main.assert_called_once() + + def test_create_parser(): with ExtensionPointContext(): parser = create_parser('colcon_core.environment_variable') From 4f3ef46645257ef2464d1a7dbaf2976bd62443c7 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Thu, 25 Jul 2024 15:13:10 -0500 Subject: [PATCH 2/4] Pass verb class type instead of class instance --- colcon_core/command.py | 11 ++++++----- test/test_command.py | 15 +++++++-------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/colcon_core/command.py b/colcon_core/command.py index 6d8565a5..41548dc3 100644 --- a/colcon_core/command.py +++ b/colcon_core/command.py @@ -117,7 +117,7 @@ def main( :param str verb_group_name: The extension point group name for verbs :param str environment_variable_group_name: The extension point group name for environment variables - :param VerbExtensionPoint default_verb: The verb to invoke if no explicit + :param Type default_verb: The verb class type to invoke if no explicit verb was provided on the command line :returns: The return code """ @@ -159,10 +159,11 @@ def _main( parser = create_parser(environment_variable_group_name) if default_verb is not None: + default_verb_instance = default_verb() parser.set_defaults( - verb_parser=parser, verb_extension=default_verb, - main=default_verb.main) - add_parser_arguments(parser, default_verb) + verb_parser=parser, verb_extension=default_verb_instance, + main=default_verb_instance.main) + add_parser_arguments(parser, default_verb_instance) verb_extensions = get_verb_extensions(group_name=verb_group_name) @@ -189,7 +190,7 @@ def _main( colcon_logger.debug(f'Parsed command line arguments: {args}') # error: no verb provided - if args.verb_name is None and default_verb is None: + if not getattr(args, 'main', None): print(parser.format_usage()) return 'Error: No verb provided' diff --git a/test/test_command.py b/test/test_command.py index b03616a4..e4339fe3 100644 --- a/test/test_command.py +++ b/test/test_command.py @@ -97,27 +97,26 @@ def test_main_no_verbs_or_env(): def test_main_default_verb(): - default_verb = Extension2() - default_verb.main = Mock(wraps=default_verb.main) with ExtensionPointContext(): with patch( 'colcon_core.argument_parser.get_argument_parser_extensions', return_value={} ): with pytest.raises(SystemExit) as e: - main(argv=['--help'], default_verb=default_verb) + main(argv=['--help'], default_verb=Extension1) assert e.value.code == 0 with pytest.raises(SystemExit) as e: main( argv=['--log-level', 'invalid'], - default_verb=default_verb) + default_verb=Extension1) assert e.value.code == 2 - assert not main( - argv=['--log-base', '/dev/null'], - default_verb=default_verb) - default_verb.main.assert_called_once() + with patch.object(Extension1, 'main', return_value=0) as mock_main: + assert not main( + argv=['--log-base', '/dev/null'], + default_verb=Extension1) + mock_main.assert_called_once() def test_create_parser(): From 99fcb4fbac970a1d3ac7c77dc9b2043878607747 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Thu, 25 Jul 2024 15:24:38 -0500 Subject: [PATCH 3/4] fixup! Pass verb class type instead of class instance --- colcon_core/command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colcon_core/command.py b/colcon_core/command.py index 41548dc3..6ac61028 100644 --- a/colcon_core/command.py +++ b/colcon_core/command.py @@ -190,7 +190,7 @@ def _main( colcon_logger.debug(f'Parsed command line arguments: {args}') # error: no verb provided - if not getattr(args, 'main', None): + if getattr(args, 'main', None) is None: print(parser.format_usage()) return 'Error: No verb provided' From ecd8267ae1af3cf1a42522d65fb8bc312ddfca38 Mon Sep 17 00:00:00 2001 From: Scott K Logan Date: Thu, 25 Jul 2024 15:44:22 -0500 Subject: [PATCH 4/4] fixup! Pass verb class type instead of class instance --- colcon_core/command.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/colcon_core/command.py b/colcon_core/command.py index 6ac61028..84dd43cb 100644 --- a/colcon_core/command.py +++ b/colcon_core/command.py @@ -189,7 +189,7 @@ def _main( colcon_logger.debug(f'Parsed command line arguments: {args}') - # error: no verb provided + # verify that one of the verbs set the 'main' attribute to be invoked later if getattr(args, 'main', None) is None: print(parser.format_usage()) return 'Error: No verb provided'