diff --git a/ros2component/ros2component/api/__init__.py b/ros2component/ros2component/api/__init__.py index 01adcfce1..3c0b1ae01 100644 --- a/ros2component/ros2component/api/__init__.py +++ b/ros2component/ros2component/api/__init__.py @@ -14,8 +14,6 @@ from collections import namedtuple import subprocess - -from ament_index_python import get_resource from ament_index_python import get_resources from ament_index_python import has_resource @@ -32,37 +30,62 @@ from ros2pkg.api import get_executable_paths from ros2pkg.api import PackageNotFound -COMPONENTS_RESOURCE_TYPE = 'rclcpp_components' +from .component_searcher import ComponentSearcher + +COMPONENTS_RESOURCE_TYPES = ['rclcpp_components', 'rclpy_components'] + +try: + from importlib.metadata import entry_points +except ImportError: + from importlib_meatadata import entry_points + +# TODO Make the process lazy or provide a explict init function +# Try to find the loaders: +res_searchers = {} +eps = entry_points() +for component_res_type in COMPONENTS_RESOURCE_TYPES: + searcher_entrypoints = eps.get(component_res_type+'.searcher', None) + if searcher_entrypoints: + # TODO What if there are multiple registered searchers? + component_class = searcher_entrypoints[0].load() + res_searchers[component_res_type] = component_class(component_res_type) def get_package_names_with_component_types(): """Get the names of all packages that register component types in the ament index.""" - return list(get_resources(COMPONENTS_RESOURCE_TYPE).keys()) + package_names = dict() + for res_type in COMPONENTS_RESOURCE_TYPES: + package_names[res_type] = list(get_resources(res_type).keys()) + return package_names def get_package_component_types(*, package_name=None): """ - Get all component types registered in the ament index for the given package. + Get all component types registered for the given package. :param package_name: whose component types are to be retrieved. :return: a list of component type names. """ - if not has_resource(COMPONENTS_RESOURCE_TYPE, package_name): - return [] - component_registry, _ = get_resource(COMPONENTS_RESOURCE_TYPE, package_name) - return [line.split(';')[0] for line in component_registry.splitlines()] + component_types = [] + global res_searchers + for resource_component_type, searcher in res_searchers.items(): + if has_resource(resource_component_type, package_name): + component_types.extend(searcher.get_package_component_types(package_name)) + return component_types def get_registered_component_types(): """ - Get all component types registered in the ament index. + Get all registered component types. :return: a list of (package name, component type names) tuples. """ - return [ - (package_name, get_package_component_types(package_name=package_name)) - for package_name in get_package_names_with_component_types() - ] + global res_searchers + registered_component_types = [] + for res_type, searcher in res_searchers.items(): + component_types = searcher.get_component_types() + registered_component_types.extend(component_types) + return registered_component_types ComponentInfo = namedtuple('Component', ('uid', 'name')) @@ -395,12 +418,12 @@ def add_component_arguments(parser): ) -def run_standalone_container(*, container_node_name): +def run_standalone_container(*, container_node_name, component_type='rclcpp_components'): """Run a standalone component container.""" try: - paths = get_executable_paths(package_name='rclcpp_components') + paths = get_executable_paths(package_name=component_type) except PackageNotFound: - raise RuntimeError("Package 'rclcpp_components' not found") + raise RuntimeError("Package '%s' not found" % component_type) executable_path = next((p for p in paths if 'component_container' in p), None) if executable_path is None: diff --git a/ros2component/ros2component/api/component_searcher.py b/ros2component/ros2component/api/component_searcher.py new file mode 100644 index 000000000..28208b3ae --- /dev/null +++ b/ros2component/ros2component/api/component_searcher.py @@ -0,0 +1,32 @@ +# Copyright 2020 Open Source Robotics Foundation, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +class ComponentSearcher: + """An abstract class providing interfaces to get component types""" + + def __init__(self, component_resource_type): + self.component_resource_type = component_resource_type + + def get_component_types(self): + """Get the registered component types + :return: a list of component types in the tuple(res type/package name, component name) + """ + pass + + def get_package_component_types(self, package_name): + """Get the registered component types in a package + :param package_name: whose component types are to be retrieved. + :return: a list of component types in the tuple(res type/package name, component name) + """ + pass diff --git a/ros2component/ros2component/verb/standalone.py b/ros2component/ros2component/verb/standalone.py index 814311662..835ccf093 100644 --- a/ros2component/ros2component/verb/standalone.py +++ b/ros2component/ros2component/verb/standalone.py @@ -36,9 +36,15 @@ def add_arguments(self, parser, cli_name): default='standalone_container_' + uuid.uuid4().hex[:12], help='Name of the standalone container node to be run' ) + parser.add_argument( + '-t', '--component-type', + default='rclcpp_components', + help='Component resource types, typically in the format rcl_components' + ) def main(self, *, args): - container = run_standalone_container(container_node_name=args.container_node_name) + container = run_standalone_container(container_node_name=args.container_node_name, + component_type=args.component_type) with DirectNode(args) as node: try: