Skip to content

Commit

Permalink
add ability to show and pass launch arguments on the command line
Browse files Browse the repository at this point in the history
  • Loading branch information
wjwwood committed Aug 14, 2018
1 parent ca2b8d0 commit 4fac37a
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 4 deletions.
2 changes: 2 additions & 0 deletions ros2launch/ros2launch/api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .api import LaunchFileNameCompleter
from .api import MultipleLaunchFilesError
from .api import print_a_python_launch_file
from .api import print_arguments_of_python_launch_file

__all__ = [
'get_share_file_path_from_package',
Expand All @@ -28,4 +29,5 @@
'launch_a_python_launch_file',
'MultipleLaunchFilesError',
'print_a_python_launch_file',
'print_arguments_of_python_launch_file',
]
40 changes: 39 additions & 1 deletion ros2launch/ros2launch/api/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@

"""Python package for the ros2 launch api implementation."""

from collections import OrderedDict
import os
from typing import List
from typing import Text
from typing import Tuple

from ament_index_python.packages import get_package_share_directory
from ament_index_python.packages import PackageNotFoundError
Expand Down Expand Up @@ -84,18 +88,52 @@ def print_a_python_launch_file(*, python_launch_file_path):
print(launch.LaunchIntrospector().format_launch_description(launch_description))


def print_arguments_of_python_launch_file(*, python_launch_file_path):
"""Print the arguments of a Python launch file to the console."""
launch_description = get_launch_description_from_python_launch_file(python_launch_file_path)
print(
"Arguments for launch file '{}' (pass arguments as '<name>:=<value>'):"
.format(os.path.basename(python_launch_file_path)))
for argument_action in launch_description.get_launch_arguments():
msg = ' '
msg += argument_action.name
if argument_action.default_value is not None:
default_str = ' + '.join([token.describe() for token in argument_action.default_value])
msg += ' (default: {})'.format(default_str)
msg += ':\n '
msg += argument_action.description
print(msg)


def parse_launch_arguments(launch_arguments: List[Text]) -> List[Tuple[Text, Text]]:
"""Parse the given launch arguments from the command line, into list of tuples for launch."""
parsed_launch_arguments = OrderedDict() # type: ignore
for argument in launch_arguments:
count = argument.count(':=')
if count == 0 or argument.startswith(':=') or (count == 1 and argument.endswith(':=')):
raise RuntimeError(
"malformed launch argument '{}', expected format '<name>:=<value>'"
.format(argument))
name, value = argument.split(':=', maxsplit=1)
parsed_launch_arguments[name] = value # last one wins is intentional
return parsed_launch_arguments.items()


def launch_a_python_launch_file(*, python_launch_file_path, launch_file_arguments, debug=False):
"""Launch a given Python launch file (by path) and pass it the given launch file arguments."""
launch_service = launch.LaunchService(argv=launch_file_arguments, debug=debug)
launch_service.include_launch_description(
launch_ros.get_default_launch_description(prefix_output_with_name=False))
parsed_launch_arguments = parse_launch_arguments(launch_file_arguments)
# Include the user provided launch file using IncludeLaunchDescription so that the
# location of the current launch file is set.
launch_description = launch.LaunchDescription([
launch.actions.IncludeLaunchDescription(
launch.launch_description_sources.PythonLaunchDescriptionSource(
python_launch_file_path
))
),
launch_arguments=parsed_launch_arguments,
),
])
launch_service.include_launch_description(launch_description)
return launch_service.run()
Expand Down
41 changes: 38 additions & 3 deletions ros2launch/ros2launch/command/launch.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import os
import sys

from ament_index_python.packages import get_package_prefix
from ament_index_python.packages import PackageNotFoundError
from ros2cli.command import CommandExtension
from ros2launch.api import get_share_file_path_from_package
Expand All @@ -24,6 +25,7 @@
from ros2launch.api import LaunchFileNameCompleter
from ros2launch.api import MultipleLaunchFilesError
from ros2launch.api import print_a_python_launch_file
from ros2launch.api import print_arguments_of_python_launch_file
from ros2pkg.api import package_name_completer


Expand All @@ -35,9 +37,13 @@ def add_arguments(self, parser, cli_name):
parser.add_argument(
'-d', '--debug', default=False, action='store_true',
help='Put the launch system in debug mode, provides more verbose output.')
parser.add_argument(
command_group = parser.add_mutually_exclusive_group()
command_group.add_argument(
'-p', '--print', '--print-description', default=False, action='store_true',
help='Print the launch description to the console without launching it.')
command_group.add_argument(
'-s', '--show-args', '--show-arguments', default=False, action='store_true',
help='Show arguments that may be given to the launch file.')
arg = parser.add_argument(
'package_name',
help='Name of the ROS package which contains the launch file')
Expand All @@ -47,21 +53,45 @@ def add_arguments(self, parser, cli_name):
# TODO(wjwwood) make this not optional when full launch path is supported.
nargs='?',
help='Name of the launch file')
arg = parser.add_argument(
'launch_arguments',
nargs='*',
help="Arguments to the launch file; '<name>:=<value>' (for duplicates, last one wins)")
arg.completer = LaunchFileNameCompleter()
parser.add_argument(
'argv', nargs=REMAINDER,
help='Pass arbitrary arguments to the launch file')

def main(self, *, parser, args):
"""Entry point for CLI program."""
mode = 'pkg file'
if args.launch_file_name is None:
# If only one argument passed, use single file mode.
mode = 'single file'
else:
# Test if first argument is a package, and if not change to single
# file mode, but only if the file exists.
try:
get_package_prefix(args.package_name)
except PackageNotFoundError:
if os.path.exists(args.package_name):
mode = 'single file'

path = None
launch_arguments = []
if mode == 'single file':
# TODO(wjwwood): figure out how to have argparse and argcomplete
# handle this, for now, hidden feature.
if os.path.exists(args.package_name):
path = args.package_name
else:
return 'No launch file supplied'
else:

if args.launch_file_name is not None:
# Since in single file mode, the "launch file" argument is
# actually part of the launch arguments, if set.
launch_arguments.append(args.launch_file_name)
elif mode == 'pkg file':
try:
path = get_share_file_path_from_package(
package_name=args.package_name,
Expand All @@ -71,13 +101,18 @@ def main(self, *, parser, args):
"Package '{}' not found: {}".format(args.package_name, exc))
except (FileNotFoundError, MultipleLaunchFilesError) as exc:
raise RuntimeError(str(exc))
else:
raise RuntimeError('unexpected mode')
launch_arguments.extend(args.launch_arguments)
try:
if args.print:
return print_a_python_launch_file(python_launch_file_path=path)
elif args.show_args:
return print_arguments_of_python_launch_file(python_launch_file_path=path)
else:
return launch_a_python_launch_file(
python_launch_file_path=path,
launch_file_arguments=args.argv,
launch_file_arguments=launch_arguments,
debug=args.debug
)
except SyntaxError:
Expand Down

0 comments on commit 4fac37a

Please sign in to comment.