Skip to content

Commit

Permalink
Types for rosidl_adapter (#828)
Browse files Browse the repository at this point in the history
* type for rosidl_adapter

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>

* re-run CI

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>

* re-run CI

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>

* use ament_cmake_mypy instead

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>

---------

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
InvincibleRMC and mergify[bot] authored Nov 1, 2024
1 parent ecb5d12 commit b6a890c
Show file tree
Hide file tree
Showing 29 changed files with 266 additions and 179 deletions.
1 change: 1 addition & 0 deletions rosidl_adapter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ ament_python_install_package(${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_cmake_pytest REQUIRED)
find_package(ament_lint_auto REQUIRED)
find_package(ament_cmake_mypy REQUIRED)
ament_lint_auto_find_test_dependencies()
ament_add_pytest_test(pytest test)
endif()
Expand Down
1 change: 1 addition & 0 deletions rosidl_adapter/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<exec_depend>python3-empy</exec_depend>
<exec_depend>rosidl_cli</exec_depend>

<test_depend>ament_cmake_mypy</test_depend>
<test_depend>ament_cmake_pytest</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>ament_lint_auto</test_depend>
Expand Down
5 changes: 4 additions & 1 deletion rosidl_adapter/rosidl_adapter/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path

def convert_to_idl(package_dir, package_name, interface_file, output_dir):

def convert_to_idl(package_dir: Path, package_name: str, interface_file: Path,
output_dir: Path) -> Path:
if interface_file.suffix == '.msg':
from rosidl_adapter.msg import convert_msg_to_idl
return convert_msg_to_idl(
Expand Down
3 changes: 2 additions & 1 deletion rosidl_adapter/rosidl_adapter/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@

from rosidl_adapter.main import main

sys.exit(main())
main()
sys.exit()
9 changes: 6 additions & 3 deletions rosidl_adapter/rosidl_adapter/action/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from pathlib import Path

from rosidl_adapter.parser import parse_action_string
from rosidl_adapter.resource import expand_template
from rosidl_adapter.resource import ActionData, expand_template


def convert_action_to_idl(package_dir, package_name, input_file, output_dir):
def convert_action_to_idl(package_dir: Path, package_name: str, input_file: Path,
output_dir: Path) -> Path:
assert package_dir.is_absolute()
assert not input_file.is_absolute()
assert input_file.suffix == '.action'
Expand All @@ -30,7 +33,7 @@ def convert_action_to_idl(package_dir, package_name, input_file, output_dir):
output_file = output_dir / input_file.with_suffix('.idl').name
abs_output_file = output_file.absolute()
print(f'Writing output file: {abs_output_file}')
data = {
data: ActionData = {
'pkg_name': package_name,
'relative_input_file': input_file.as_posix(),
'action': action,
Expand Down
41 changes: 26 additions & 15 deletions rosidl_adapter/rosidl_adapter/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@
# limitations under the License.

import argparse
import pathlib
from pathlib import Path
import sys
from typing import Callable, List, Literal, TYPE_CHECKING

from catkin_pkg.package import package_exists_at
from catkin_pkg.package import parse_package
Expand All @@ -27,7 +28,18 @@
from rosidl_cli.command.translate.extensions import TranslateCommandExtension


def convert_files_to_idl(extension, conversion_function, argv=sys.argv[1:]):
if TYPE_CHECKING:
from typing_extensions import TypeAlias

ConversionFunctionType: TypeAlias = Callable[[Path, str, Path, Path], Path]


def convert_files_to_idl(
extension: Literal['.msg', '.srv', '.action'],
conversion_function: 'ConversionFunctionType',
argv: List[str] = sys.argv[1:]
) -> None:

parser = argparse.ArgumentParser(
description=f'Convert {extension} files to .idl')
parser.add_argument(
Expand All @@ -36,7 +48,7 @@ def convert_files_to_idl(extension, conversion_function, argv=sys.argv[1:]):
args = parser.parse_args(argv)

for interface_file in args.interface_files:
interface_file = pathlib.Path(interface_file)
interface_file = Path(interface_file)
package_dir = interface_file.parent.absolute()
while (
len(package_dir.parents) and
Expand All @@ -48,8 +60,7 @@ def convert_files_to_idl(extension, conversion_function, argv=sys.argv[1:]):
f"Could not find package for '{interface_file}'",
file=sys.stderr)
continue
warnings = []
pkg = parse_package(package_dir, warnings=warnings)
pkg = parse_package(package_dir, warnings=[])

conversion_function(
package_dir, pkg.name,
Expand All @@ -63,14 +74,14 @@ class TranslateToIDL(TranslateCommandExtension):

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]:
translated_interface_files = []
for interface_file in interface_files:
prefix, interface_file = interface_path_as_tuple(interface_file)
for interface_file_str in interface_files:
prefix, interface_file = interface_path_as_tuple(interface_file_str)
output_dir = output_path / interface_file.parent
translated_interface_file = self.conversion_function(
prefix, package_name, interface_file, output_dir)
Expand All @@ -87,7 +98,7 @@ class TranslateMsgToIDL(TranslateToIDL):
input_format = 'msg'

@property
def conversion_function(self):
def conversion_function(self) -> 'ConversionFunctionType':
return convert_msg_to_idl


Expand All @@ -96,13 +107,13 @@ class TranslateSrvToIDL(TranslateToIDL):
input_format = 'srv'

@property
def conversion_function(self):
def conversion_function(self) -> 'ConversionFunctionType':
return convert_srv_to_idl


class TranslateActionToIDL(TranslateToIDL):
input_format = 'action'

@property
def conversion_function(self):
def conversion_function(self) -> 'ConversionFunctionType':
return convert_action_to_idl
3 changes: 2 additions & 1 deletion rosidl_adapter/rosidl_adapter/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@
import os
import pathlib
import sys
from typing import List


from rosidl_adapter import convert_to_idl


def main(argv=sys.argv[1:]):
def main(argv: List[str] = sys.argv[1:]) -> None:
parser = argparse.ArgumentParser(
description='Convert interface files to .idl')
parser.add_argument(
Expand Down
24 changes: 14 additions & 10 deletions rosidl_adapter/rosidl_adapter/msg/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@
# See the License for the specific language governing permissions and
# limitations under the License.

from rosidl_adapter.parser import parse_message_string
from rosidl_adapter.resource import expand_template
from pathlib import Path
from typing import Final, Optional, Union

from rosidl_adapter.parser import BaseType, parse_message_string, Type
from rosidl_adapter.resource import expand_template, MsgData

def convert_msg_to_idl(package_dir, package_name, input_file, output_dir):

def convert_msg_to_idl(package_dir: Path, package_name: str, input_file: Path,
output_dir: Path) -> Path:
assert package_dir.is_absolute()
assert not input_file.is_absolute()
assert input_file.suffix == '.msg'
Expand All @@ -30,7 +34,7 @@ def convert_msg_to_idl(package_dir, package_name, input_file, output_dir):
output_file = output_dir / input_file.with_suffix('.idl').name
abs_output_file = output_file.absolute()
print(f'Writing output file: {abs_output_file}')
data = {
data: MsgData = {
'pkg_name': package_name,
'relative_input_file': input_file.as_posix(),
'msg': msg,
Expand All @@ -40,7 +44,7 @@ def convert_msg_to_idl(package_dir, package_name, input_file, output_dir):
return output_file


MSG_TYPE_TO_IDL = {
MSG_TYPE_TO_IDL: Final = {
'bool': 'boolean',
'byte': 'octet',
'char': 'uint8',
Expand All @@ -59,7 +63,7 @@ def convert_msg_to_idl(package_dir, package_name, input_file, output_dir):
}


def to_idl_literal(idl_type, value):
def to_idl_literal(idl_type: str, value: str) -> str:
if idl_type[-1] == ']' or idl_type.startswith('sequence<'):
content = repr(tuple(value)).replace('\\', r'\\').replace('"', r'\"')
return f'"{content}"'
Expand All @@ -73,24 +77,24 @@ def to_idl_literal(idl_type, value):
return value


def string_to_idl_string_literal(string):
def string_to_idl_string_literal(string: str) -> str:
"""Convert string to character literal as described in IDL 4.2 section 7.2.6.3 ."""
estr = string.encode().decode('unicode_escape')
estr = estr.replace('"', r'\"')
return '"{0}"'.format(estr)


def string_to_idl_wstring_literal(string):
def string_to_idl_wstring_literal(string: str) -> str:
return string_to_idl_string_literal(string)


def get_include_file(base_type):
def get_include_file(base_type: BaseType) -> Optional[str]:
if base_type.is_primitive_type():
return None
return f'{base_type.pkg_name}/msg/{base_type.type}.idl'


def get_idl_type(type_):
def get_idl_type(type_: Union[str, Type]) -> str:
if isinstance(type_, str):
identifier = MSG_TYPE_TO_IDL[type_]
elif type_.is_primitive_type():
Expand Down
Loading

0 comments on commit b6a890c

Please sign in to comment.