Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ros2plugin #165

Open
wants to merge 2 commits into
base: rolling
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions ros2plugin/package.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>ros2plugin</name>
<version>0.7.4</version>
<description>
The plugin command for ROS 2 command line tools.
</description>
<maintainer email="[email protected]">Jeremie Deray</maintainer>
<license>Apache License 2.0</license>

<exec_depend>ament_index_python</exec_depend>
<exec_depend>rclpy</exec_depend>
<exec_depend>ros2cli</exec_depend>
<exec_depend>ros2pkg</exec_depend>

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>ament_xmllint</test_depend>
<test_depend>python3-pytest</test_depend>

<export>
<build_type>ament_python</build_type>
</export>
</package>
Empty file.
Empty file.
81 changes: 81 additions & 0 deletions ros2plugin/ros2plugin/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# Copyright 2019 Canonical Ltd.
#
# 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.

from ament_index_python.resources import get_resource
from ament_index_python.resources import get_resource_types
from ament_index_python.resources import get_resources
from ament_index_python.resources import has_resource

PLUGIN_RESOURCE_TYPE = '__pluginlib__plugin'


def is_plugin_resource_type(resource_type):
"""
Check if resource_type has the plugin extension.

:param str resource_type: the resource type name to be evaluated.

:return: a boolean, True if resource_type has the plugin extension.
"""
assert resource_type, 'The resource type must not be empty'
return PLUGIN_RESOURCE_TYPE in resource_type


def get_registered_plugin_resource_list():
"""
Get all plugin resources registered in the ament index.

:return: a filtered list containing the plugin ressouce types.
"""
return filter(is_plugin_resource_type, get_resource_types())


def get_package_names_with_plugin_resource_types():
"""
Get the names of all packages that register a plugin resource in the ament index.

:return: a list of packages exporting plugins.
"""
packages = []
for plugin in get_registered_plugin_resource_list():
packages += list(get_resources(plugin).keys())
return packages


def get_package_plugin_resource(*, package_name=None):
"""
Get all plugin resources registered in the ament index for the given package.

:param package_name: whose component types are to be retrieved.
:return: a list of plugin resources relative path.
"""
plugin_resources = get_registered_plugin_resource_list()
package_plugins = []
for plugin_resource in plugin_resources:
if has_resource(plugin_resource, package_name):
component_registry, _ = get_resource(plugin_resource, package_name)
package_plugins += [line.split(';')[0] for line in component_registry.splitlines()]
return package_plugins


def get_registered_plugin_resources():
"""
Get all plugin resources registered in the ament index.

:return: a list of (package name, plugin type names) tuples.
"""
return [
(package_name, get_package_plugin_resource(package_name=package_name))
for package_name in get_package_names_with_plugin_resource_types()
]
Empty file.
39 changes: 39 additions & 0 deletions ros2plugin/ros2plugin/command/plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Copyright 2019 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.

from ros2cli.command import add_subparsers
from ros2cli.command import CommandExtension
from ros2cli.verb import get_verb_extensions


class PluginCommand(CommandExtension):
"""Various plugin related sub-commands."""

def add_arguments(self, parser, cli_name):
self._subparser = parser
# get verb extensions and let them add their arguments
verb_extensions = get_verb_extensions('ros2plugin.verb')
add_subparsers(
parser, cli_name, '_verb', verb_extensions, required=False)

def main(self, *, parser, args):
if not hasattr(args, '_verb'):
# in case no verb was passed
self._subparser.print_help()
return 0

extension = getattr(args, '_verb')

# call the verb's main method
return extension.main(args=args)
44 changes: 44 additions & 0 deletions ros2plugin/ros2plugin/verb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright 2019 Canonical Ltd.
#
# 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.

from ros2cli.plugin_system import PLUGIN_SYSTEM_VERSION
from ros2cli.plugin_system import satisfies_version


class VerbExtension:
"""
The extension point for 'plugin' verb extensions.

The following properties must be defined:
* `NAME` (will be set to the entry point name)

The following methods must be defined:
* `main`

The following methods can be defined:
* `add_arguments`
"""

NAME = None
EXTENSION_POINT_VERSION = '0.1'

def __init__(self):
super(VerbExtension, self).__init__()
satisfies_version(PLUGIN_SYSTEM_VERSION, '^0.1')

def add_arguments(self, parser, cli_name):
pass

def main(self, *, args):
raise NotImplementedError()
82 changes: 82 additions & 0 deletions ros2plugin/ros2plugin/verb/list.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# Copyright 2019 Canonical Ltd.
#
# 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.

from collections import namedtuple

import os
import xml.etree.ElementTree as ET

from ament_index_python import get_package_prefix
from ament_index_python import PackageNotFoundError
from ros2cli.node.strategy import add_arguments

from ros2plugin.api import get_registered_plugin_resources
from ros2plugin.verb import VerbExtension


PluginInfo = namedtuple('Plugin', ('name', 'type', 'base'))


class ListVerb(VerbExtension):
"""Output a list of plugins."""

def add_arguments(self, parser, cli_name):
add_arguments(parser)
parser.add_argument(
'--packages', action='store_true',
help='List the packages that register plugins')
parser.add_argument(
'--package', type=str,
help='Name of the package to list plugins from')

def main(self, *, args):
plugin_resources = get_registered_plugin_resources()

if args.package:
plugin_resources

if args.packages:
for package_name, package_plugin_resources in sorted(plugin_resources):
print(package_name + ':')
if any(package_plugin_resources):
print(*['\t' + r for r in package_plugin_resources], sep='\n')
return

for package_name, package_plugin_resources in sorted(plugin_resources):
plugins = []
print(package_name + ':')
for package_plugin_resource in package_plugin_resources:
try:
package_prefix = get_package_prefix(package_name)
except PackageNotFoundError:
print('Package ' + package_name + ' not found.')

plugin_xml = os.path.join(package_prefix, package_plugin_resource)
if not os.path.isfile(plugin_xml):
print('XML manifest ' + os.path.basename(plugin_xml) + ' not found.')

tree = ET.parse(plugin_xml)

for e in tree.iter():
if e.tag == 'class':
try:
plugin_name = e.attrib['name']
except KeyError:
plugin_name = e.attrib['type']
plugins.append(PluginInfo(
plugin_name, e.attrib['type'], e.attrib['base_class_type'])
)

if any(plugins):
print(*['\t' + str(p) for p in plugins], sep='\n')
46 changes: 46 additions & 0 deletions ros2plugin/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from setuptools import find_packages
from setuptools import setup

package_name = 'ros2plugin'

setup(
name=package_name,
version='0.7.4',
packages=find_packages(exclude=['test']),
data_files=[
('share/' + package_name, ['package.xml']),
('share/ament_index/resource_index/packages',
['resource/' + package_name]),
],
install_requires=['ros2cli'],
zip_safe=True,
author='Jeremie Deray',
author_email='[email protected]',
maintainer='Jeremie Deray',
maintainer_email='[email protected]',
url='https://github.com/ros2/ros2cli/tree/master/ros2plugin',
download_url='https://github.com/ros2/ros2cli/releases',
keywords=[],
classifiers=[
'Environment :: Console',
'Intended Audience :: Developers',
'License :: OSI Approved :: Apache Software License',
'Programming Language :: Python',
],
description='The plugin command for ROS 2 command line tools.',
long_description="""\
The package provides the plugin command for the ROS 2 command line tools.""",
license='Apache License, Version 2.0',
tests_require=['pytest'],
entry_points={
'ros2cli.command': [
'plugin = ros2plugin.command.plugin:PluginCommand',
],
'ros2cli.extension_point': [
'ros2plugin.verb = ros2plugin.verb:VerbExtension',
],
'ros2plugin.verb': [
'list = ros2plugin.verb.list:ListVerb',
],
}
)
23 changes: 23 additions & 0 deletions ros2plugin/test/test_copyright.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2017 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.

from ament_copyright.main import main
import pytest


@pytest.mark.copyright
@pytest.mark.linter
def test_copyright():
rc = main(argv=['.', 'test'])
assert rc == 0, 'Found errors'
23 changes: 23 additions & 0 deletions ros2plugin/test/test_flake8.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2017 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.

from ament_flake8.main import main
import pytest


@pytest.mark.flake8
@pytest.mark.linter
def test_flake8():
rc = main(argv=[])
assert rc == 0, 'Found errors'
23 changes: 23 additions & 0 deletions ros2plugin/test/test_pep257.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Copyright 2017 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.

from ament_pep257.main import main
import pytest


@pytest.mark.linter
@pytest.mark.pep257
def test_pep257():
rc = main(argv=[])
assert rc == 0, 'Found code style errors / warnings'
Loading