Skip to content

Commit

Permalink
Add support for command invocation with a 'default verb' (#656)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cottsay authored Jul 25, 2024
1 parent 5c56e00 commit 633ae37
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 6 deletions.
30 changes: 24 additions & 6 deletions colcon_core/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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 Type default_verb: The verb class type 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:
Expand All @@ -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)
Expand All @@ -151,6 +158,13 @@ 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_instance,
main=default_verb_instance.main)
add_parser_arguments(parser, default_verb_instance)

verb_extensions = get_verb_extensions(group_name=verb_group_name)

# add subparsers for all verb extensions but without arguments for now
Expand All @@ -163,7 +177,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)
Expand All @@ -175,18 +189,22 @@ def _main(

colcon_logger.debug(f'Parsed command line arguments: {args}')

# error: no verb provided
if args.verb_name is None:
# 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'

# 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()
Expand Down
23 changes: 23 additions & 0 deletions test/test_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,29 @@ def test_main_no_verbs_or_env():
assert e.value.code == 0


def test_main_default_verb():
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=Extension1)
assert e.value.code == 0

with pytest.raises(SystemExit) as e:
main(
argv=['--log-level', 'invalid'],
default_verb=Extension1)
assert e.value.code == 2

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():
with ExtensionPointContext():
parser = create_parser('colcon_core.environment_variable')
Expand Down

0 comments on commit 633ae37

Please sign in to comment.