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

Rosidl cli types with specs_set fix #831

Open
wants to merge 14 commits into
base: rolling
Choose a base branch
from
1 change: 1 addition & 0 deletions rosidl_cli/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

<test_depend>ament_copyright</test_depend>
<test_depend>ament_flake8</test_depend>
<test_depend>ament_mypy</test_depend>
<test_depend>ament_pep257</test_depend>
<test_depend>ament_xmllint</test_depend>
<test_depend>python3-pytest</test_depend>
Expand Down
Empty file added rosidl_cli/py.typed
Empty file.
12 changes: 9 additions & 3 deletions rosidl_cli/rosidl_cli/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,18 @@

import argparse
import signal
from typing import Any, List, Union

from rosidl_cli.command.generate import GenerateCommand
from rosidl_cli.command.translate import TranslateCommand
from rosidl_cli.common import get_first_line_doc


def add_subparsers(parser, cli_name, commands):
def add_subparsers(
parser: argparse.ArgumentParser,
cli_name: str,
commands: List[Union[GenerateCommand, TranslateCommand]]
) -> argparse._SubParsersAction[argparse.ArgumentParser]:
"""
Create argparse subparser for each command.

Expand Down Expand Up @@ -63,7 +68,7 @@ def add_subparsers(parser, cli_name, commands):
return subparser


def main():
def main() -> Union[str, signal.Signals, Any]:
script_name = 'rosidl'
description = f'{script_name} is an extensible command-line tool ' \
'for ROS interface generation.'
Expand All @@ -74,7 +79,8 @@ def main():
formatter_class=argparse.RawDescriptionHelpFormatter
)

commands = [GenerateCommand(), TranslateCommand()]
commands: List[Union[GenerateCommand, TranslateCommand]] = \
[GenerateCommand(), TranslateCommand()]

# add arguments for command extension(s)
add_subparsers(
Expand Down
6 changes: 4 additions & 2 deletions rosidl_cli/rosidl_cli/command/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse


class Command:
"""
Expand All @@ -22,8 +24,8 @@ class Command:
* `add_arguments`
"""

def add_arguments(self, parser):
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
pass

def main(self, *, parser, args):
def main(self, *, args: argparse.Namespace) -> None:
raise NotImplementedError()
5 changes: 3 additions & 2 deletions rosidl_cli/rosidl_cli/command/generate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import pathlib

from rosidl_cli.command import Command
Expand All @@ -24,7 +25,7 @@ class GenerateCommand(Command):

name = 'generate'

def add_arguments(self, parser):
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
'-o', '--output-path', metavar='PATH',
type=pathlib.Path, default=None,
Expand All @@ -50,7 +51,7 @@ def add_arguments(self, parser):
"If prefixed by another path followed by a colon ':', "
'path resolution is performed against such path.'))

def main(self, *, args):
def main(self, *, args: argparse.Namespace) -> None:
generate(
package_name=args.package_name,
interface_files=args.interface_files,
Expand Down
18 changes: 10 additions & 8 deletions rosidl_cli/rosidl_cli/command/generate/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@

import os
import pathlib
from typing import List, Optional

from .extensions import GenerateCommandExtension
from .extensions import load_type_extensions
from .extensions import load_typesupport_extensions


def generate(
*,
package_name,
interface_files,
include_paths=None,
output_path=None,
types=None,
typesupports=None
):
package_name: str,
interface_files: List[str],
include_paths: Optional[List[str]] = None,
output_path: Optional[pathlib.Path] = None,
types: Optional[List[str]] = None,
typesupports: Optional[List[str]] = None
) -> List[List[str]]:
"""
Generate source code from interface definition files.

Expand Down Expand Up @@ -60,7 +62,7 @@ def generate(
:returns: list of lists of paths to generated source code files,
one group per type or type support extension invoked
"""
extensions = []
extensions: List[GenerateCommandExtension] = []

unspecific_generation = not types and not typesupports

Expand Down
27 changes: 18 additions & 9 deletions rosidl_cli/rosidl_cli/command/generate/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path
from typing import cast, List, Optional

from rosidl_cli.extensions import Extension
from rosidl_cli.extensions import load_extensions

Expand All @@ -26,11 +29,11 @@ class GenerateCommandExtension(Extension):

def generate(
self,
package_name,
interface_files,
include_paths,
output_path
):
package_name: str,
interface_files: List[str],
include_paths: List[str],
output_path: Path
) -> List[str]:
"""
Generate source code.

Expand All @@ -48,11 +51,17 @@ def generate(
raise NotImplementedError()


def load_type_extensions(**kwargs):
def load_type_extensions(*, specs: Optional[List[str]],
strict: bool) -> List[GenerateCommandExtension]:
"""Load extensions for type representation source code generation."""
return load_extensions('rosidl_cli.command.generate.type_extensions', **kwargs)
extensions = load_extensions('rosidl_cli.command.generate.type_extensions', specs=specs,
strict=strict)
return cast(List[GenerateCommandExtension], extensions)


def load_typesupport_extensions(**kwargs):
def load_typesupport_extensions(*, specs: Optional[List[str]], strict: bool
) -> List[GenerateCommandExtension]:
"""Load extensions for type support source code generation."""
return load_extensions('rosidl_cli.command.generate.typesupport_extensions', **kwargs)
extensions = load_extensions('rosidl_cli.command.generate.typesupport_extensions',
specs=specs, strict=strict)
return cast(List[GenerateCommandExtension], extensions)
49 changes: 26 additions & 23 deletions rosidl_cli/rosidl_cli/command/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,10 @@
import os
import pathlib
import tempfile
from typing import Generator, List, Tuple


def package_name_from_interface_file_path(path):
def package_name_from_interface_file_path(path: pathlib.Path) -> str:
"""
Derive ROS package name from a ROS interface definition file path.

Expand All @@ -29,7 +30,7 @@ def package_name_from_interface_file_path(path):
return pathlib.Path(os.path.abspath(path)).parents[1].name


def dependencies_from_include_paths(include_paths):
def dependencies_from_include_paths(include_paths: List[str]) -> List[str]:
"""
Collect dependencies' ROS interface definition files from include paths.

Expand All @@ -45,7 +46,7 @@ def dependencies_from_include_paths(include_paths):
})


def interface_path_as_tuple(path):
def interface_path_as_tuple(path: str) -> Tuple[pathlib.Path, pathlib.Path]:
"""
Express interface definition file path as an (absolute prefix, relative path) tuple.

Expand All @@ -61,41 +62,43 @@ def interface_path_as_tuple(path):
"""
path_as_string = str(path)
if ':' not in path_as_string:
prefix = pathlib.Path.cwd()
prefix_path = pathlib.Path.cwd()
else:
prefix, _, path = path_as_string.rpartition(':')
prefix = pathlib.Path(os.path.abspath(prefix))
path = pathlib.Path(path)
if path.is_absolute():
prefix_path = pathlib.Path(os.path.abspath(prefix))
path_as_path = pathlib.Path(path)
if path_as_path.is_absolute():
raise ValueError('Interface definition file path '
f"'{path}' cannot be absolute")
return prefix, path
f"'{path_as_path}' cannot be absolute")
return prefix_path, path_as_path


def idl_tuples_from_interface_files(interface_files):
def idl_tuples_from_interface_files(
interface_files: List[str]
) -> List[str]:
"""
Express ROS interface definition file paths as IDL tuples.

An IDL tuple is a relative path prefixed by an absolute path against
which to resolve it followed by a colon ':'. This function then applies
the same logic as `interface_path_as_tuple`.
"""
idl_tuples = []
for path in interface_files:
prefix, path = interface_path_as_tuple(path)
idl_tuples: List[str] = []
for interface_path in interface_files:
prefix, path = interface_path_as_tuple(interface_path)
idl_tuples.append(f'{prefix}:{path.as_posix()}')
return idl_tuples


@contextlib.contextmanager
def legacy_generator_arguments_file(
*,
package_name,
interface_files,
include_paths,
templates_path,
output_path
):
package_name: str,
interface_files: List[str],
include_paths: List[str],
templates_path: str,
output_path: str
) -> Generator[str, None, None]:
"""
Generate a temporary rosidl generator arguments file.

Expand Down Expand Up @@ -138,10 +141,10 @@ def legacy_generator_arguments_file(

def generate_visibility_control_file(
*,
package_name,
template_path,
output_path
):
package_name: str,
template_path: str,
output_path: str
) -> None:
"""
Generate a visibility control file from a template.

Expand Down
6 changes: 4 additions & 2 deletions rosidl_cli/rosidl_cli/command/translate/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.

import argparse
import pathlib


from rosidl_cli.command import Command

from .api import translate
Expand All @@ -24,7 +26,7 @@ class TranslateCommand(Command):

name = 'translate'

def add_arguments(self, parser):
def add_arguments(self, parser: argparse.ArgumentParser) -> None:
parser.add_argument(
'-o', '--output-path', metavar='PATH',
type=pathlib.Path, default=None,
Expand Down Expand Up @@ -64,7 +66,7 @@ def add_arguments(self, parser):
'path resolution is performed against such path.')
)

def main(self, *, args):
def main(self, *, args: argparse.Namespace) -> None:
translate(
package_name=args.package_name,
interface_files=args.interface_files,
Expand Down
20 changes: 11 additions & 9 deletions rosidl_cli/rosidl_cli/command/translate/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,21 @@
import collections
import os
import pathlib
from typing import DefaultDict, Dict, List, Optional, Union

from .extensions import load_translate_extensions


def translate(
*,
package_name,
interface_files,
output_format,
input_format=None,
include_paths=None,
output_path=None,
translators=None
):
package_name: str,
interface_files: List[str],
output_format: str,
input_format: Optional[str] = None,
include_paths: Optional[List[str]] = None,
output_path: Optional[pathlib.Path] = None,
translators: Optional[List[str]] = None
) -> List[str]:
"""
Translate interface definition files from one format to another.

Expand Down Expand Up @@ -64,7 +65,8 @@ def translate(
raise RuntimeError('No translate extensions found')

if not input_format:
interface_files_per_format = collections.defaultdict(list)
interface_files_per_format: Union[DefaultDict[str, List[str]],
Dict[str, List[str]]] = collections.defaultdict(list)
for interface_file in interface_files:
input_format = os.path.splitext(interface_file)[-1][1:]
interface_files_per_format[input_format].append(interface_file)
Expand Down
23 changes: 15 additions & 8 deletions rosidl_cli/rosidl_cli/command/translate/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
# 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 pathlib import Path
from typing import cast, ClassVar, List, Optional

from rosidl_cli.extensions import Extension
from rosidl_cli.extensions import load_extensions
Expand All @@ -28,13 +30,16 @@ class TranslateCommandExtension(Extension):
* `translate`
"""

input_format: ClassVar[str]
output_format: ClassVar[str]

def translate(
self,
package_name,
interface_files,
include_paths,
output_path
):
package_name: str,
interface_files: List[str],
include_paths: List[str],
output_path: Path
) -> List[str]:
"""
Translate interface definition files.

Expand All @@ -57,8 +62,10 @@ def translate(
raise NotImplementedError()


def load_translate_extensions(**kwargs):
def load_translate_extensions(*, specs: Optional[List[str]], strict: bool
) -> List[TranslateCommandExtension]:
"""Load extensions for interface definition translation."""
return load_extensions(
'rosidl_cli.command.translate.extensions', **kwargs
extensions = load_extensions(
'rosidl_cli.command.translate.extensions', specs=specs, strict=strict
)
return cast(List[TranslateCommandExtension], extensions)
Loading
Loading