Skip to content

Commit 7403361

Browse files
InvincibleRMCsloretzchristophebedard
authored
Static typing for Message, Services, and Actions (#206)
Signed-off-by: Michael Carlstrom <rmc@carlstrom.com> Signed-off-by: Michael Carlstrom <36806982+InvincibleRMC@users.noreply.github.com> Signed-off-by: Shane Loretz <shane.loretz@gmail.com> Co-authored-by: Shane Loretz <shane.loretz@gmail.com> Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
1 parent 4dda0a4 commit 7403361

File tree

14 files changed

+340
-117
lines changed

14 files changed

+340
-117
lines changed

rosidl_generator_py/cmake/rosidl_generator_py_generate_interfaces.cmake

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,5 +301,10 @@ if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS)
301301
# a value of zero tells uncrustify to ignore line length
302302
MAX_LINE_LENGTH 0
303303
"${_output_path}")
304+
305+
find_package(ament_cmake_mypy REQUIRED)
306+
ament_mypy(
307+
TESTNAME "mypy_rosidl_generated_py"
308+
"${_output_path}")
304309
endif()
305310
endif()

rosidl_generator_py/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<exec_depend>ament_cmake_cppcheck</exec_depend>
3636
<exec_depend>ament_cmake_cpplint</exec_depend>
3737
<exec_depend>ament_cmake_flake8</exec_depend>
38+
<exec_depend>ament_cmake_mypy</exec_depend>
3839
<exec_depend>ament_cmake_pep257</exec_depend>
3940
<exec_depend>ament_cmake_uncrustify</exec_depend>
4041

rosidl_generator_py/py.typed

Whitespace-only changes.

rosidl_generator_py/resource/_action.py.em

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,50 @@ from rosidl_pycommon import convert_camel_case_to_lower_case_underscore
55
action_name = '_' + convert_camel_case_to_lower_case_underscore(action.namespaced_type.name)
66
module_name = '_' + convert_camel_case_to_lower_case_underscore(interface_path.stem)
77

8+
type_annotations_import_statements.add(f'from {".".join(action.namespaced_type.namespaces)}.{module_name} import {action.goal.structure.namespaced_type.name}')
9+
type_annotations_import_statements.add(f'from {".".join(action.namespaced_type.namespaces)}.{module_name} import {action.result.structure.namespaced_type.name}')
10+
type_annotations_import_statements.add(f'from {".".join(action.namespaced_type.namespaces)}.{module_name} import {action.feedback.structure.namespaced_type.name}')
11+
812
TEMPLATE(
913
'_msg.py.em',
1014
package_name=package_name, interface_path=interface_path,
11-
message=action.goal, import_statements=import_statements)
15+
message=action.goal, import_statements=import_statements,
16+
type_annotations_import_statements=type_annotations_import_statements)
1217
TEMPLATE(
1318
'_msg.py.em',
1419
package_name=package_name, interface_path=interface_path,
15-
message=action.result, import_statements=import_statements)
20+
message=action.result, import_statements=import_statements,
21+
type_annotations_import_statements=type_annotations_import_statements)
1622
TEMPLATE(
1723
'_msg.py.em',
1824
package_name=package_name, interface_path=interface_path,
19-
message=action.feedback, import_statements=import_statements)
25+
message=action.feedback, import_statements=import_statements,
26+
type_annotations_import_statements=type_annotations_import_statements)
2027
TEMPLATE(
2128
'_srv.py.em',
2229
package_name=package_name, interface_path=interface_path,
23-
service=action.send_goal_service, import_statements=import_statements)
30+
service=action.send_goal_service, import_statements=import_statements,
31+
type_annotations_import_statements=type_annotations_import_statements)
2432
TEMPLATE(
2533
'_srv.py.em',
2634
package_name=package_name, interface_path=interface_path,
27-
service=action.get_result_service, import_statements=import_statements)
35+
service=action.get_result_service, import_statements=import_statements,
36+
type_annotations_import_statements=type_annotations_import_statements)
2837
TEMPLATE(
2938
'_msg.py.em',
3039
package_name=package_name, interface_path=interface_path,
31-
message=action.feedback_message, import_statements=import_statements)
40+
message=action.feedback_message, import_statements=import_statements,
41+
type_annotations_import_statements=type_annotations_import_statements)
3242
}@
3343

3444

3545
class Metaclass_@(action.namespaced_type.name)(type):
3646
"""Metaclass of action '@(action.namespaced_type.name)'."""
3747

38-
_TYPE_SUPPORT = None
48+
_TYPE_SUPPORT: typing.ClassVar[typing.Optional[PyCapsule]] = None
3949

4050
@@classmethod
41-
def __import_type_support__(cls):
51+
def __import_type_support__(cls) -> None:
4252
try:
4353
from rosidl_generator_py import import_type_support
4454
module = import_type_support('@(package_name)')
@@ -92,5 +102,6 @@ class @(action.namespaced_type.name)(metaclass=Metaclass_@(action.namespaced_typ
92102
# The generic message for get the status of a goal.
93103
from action_msgs.msg._goal_status_array import GoalStatusArray as GoalStatusMessage
94104

95-
def __init__(self):
105+
# type ignore below fixed in mypy 1.0+ see mypy#10342
106+
def __init__(self) -> typing.NoReturn: # type: ignore
96107
raise NotImplementedError('Action classes can not be instantiated')

rosidl_generator_py/resource/_idl.py.em

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,15 @@
22
# with input from @(package_name):@(interface_path)
33
# generated code does not contain a copyright notice
44

5+
from __future__ import annotations
6+
7+
import collections.abc
8+
from os import getenv
9+
import typing
10+
511
# This is being done at the module level and not on the instance level to avoid looking
612
# for the same variable multiple times on each instance. This variable is not supposed to
713
# change during runtime so it makes sense to only look for it once.
8-
from os import getenv
9-
1014
ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')
1115
@
1216
@#######################################################################
@@ -19,6 +23,7 @@ ros_python_check_fields = getenv('ROS_PYTHON_CHECK_FIELDS', default='')
1923
@#######################################################################
2024
@{
2125
import_statements = set()
26+
type_annotations_import_statements = set()
2227
}@
2328
@
2429
@#######################################################################
@@ -32,7 +37,8 @@ from rosidl_parser.definition import Message
3237
TEMPLATE(
3338
'_msg.py.em',
3439
package_name=package_name, interface_path=interface_path, message=message,
35-
import_statements=import_statements)
40+
import_statements=import_statements,
41+
type_annotations_import_statements=type_annotations_import_statements)
3642
}@
3743
@[end for]@
3844
@
@@ -47,7 +53,8 @@ from rosidl_parser.definition import Service
4753
TEMPLATE(
4854
'_srv.py.em',
4955
package_name=package_name, interface_path=interface_path, service=service,
50-
import_statements=import_statements)
56+
import_statements=import_statements,
57+
type_annotations_import_statements=type_annotations_import_statements)
5158
}@
5259
@[end for]@
5360
@
@@ -62,6 +69,7 @@ from rosidl_parser.definition import Action
6269
TEMPLATE(
6370
'_action.py.em',
6471
package_name=package_name, interface_path=interface_path, action=action,
65-
import_statements=import_statements)
72+
import_statements=import_statements,
73+
type_annotations_import_statements=type_annotations_import_statements)
6674
}@
6775
@[end for]@

0 commit comments

Comments
 (0)