Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
0bd8b5b
Add helper Protocol classes for ROS interface type checking
bjsowa Aug 14, 2025
d0a2b6d
Add type annotations to ros_loader
bjsowa Aug 14, 2025
75663d6
Add type annotations to message_conversion
bjsowa Aug 14, 2025
6d6a32d
Add type annotations to rosapi
bjsowa Aug 14, 2025
b2b23be
Add type annotations to protocol
bjsowa Aug 15, 2025
46b36d2
Add type annotations and simplify logging in defragmentation
bjsowa Aug 15, 2025
c168b34
Remove ReceivedFragments singleton class, use class variable instead
bjsowa Aug 15, 2025
79deb3b
Add type annotations to fragmentation capability
bjsowa Aug 15, 2025
ec241ce
Add type variables for ROS interface types
bjsowa Aug 18, 2025
8412f36
Use Clock instead of ROSClock in message_conversion
bjsowa Aug 18, 2025
1dca703
Add type annotations to rosbridge_library internal modules
bjsowa Aug 18, 2025
4a4b9a2
Add type annotations to capabilities
bjsowa Aug 18, 2025
111fa69
Fix mypy issues in protocol
bjsowa Aug 18, 2025
36a9e11
Add type annotation to util functions
bjsowa Aug 18, 2025
4a9f1dc
Improve future type annotation
bjsowa Aug 18, 2025
c80b923
Small improvements in rosapi type annotations
bjsowa Aug 18, 2025
a9c4e3b
Pass result typevar to Future generic
bjsowa Aug 18, 2025
39f3eb1
Fix spelling mistake
bjsowa Aug 18, 2025
29dac6e
Add type annotation to capability tests
bjsowa Aug 18, 2025
aa4ec43
Fix args type for actions and services
bjsowa Aug 18, 2025
f53fe31
Add type annotation for capability message fields
bjsowa Aug 18, 2025
c7e7ecc
Reformat
bjsowa Aug 18, 2025
7c1a1a5
Disable some modules from annotation checks
bjsowa Aug 18, 2025
0aab58c
Allow empty callback in Subscription helper class
bjsowa Aug 18, 2025
6055779
Ignore encode_json assignment error
bjsowa Aug 18, 2025
0835adc
Ignore no-redef mypy errors
bjsowa Aug 18, 2025
516a031
Add type annotation and resolve mypy errors in rosbridge_library
bjsowa Aug 19, 2025
8ecf162
Merge remote-tracking branch 'origin/ros2' into refactor/add-type-ann…
bjsowa Aug 29, 2025
2918433
Add type annotations to topic/service/action glob lists
bjsowa Aug 29, 2025
2d12f2d
Add type annotation to rosbridge_server src
bjsowa Aug 29, 2025
d4eea23
Add type annotations to rosbridge_server tests
bjsowa Aug 31, 2025
e2a5326
Enable flake8-annotation linter checks
bjsowa Aug 31, 2025
1de8d21
Remove unused import
bjsowa Aug 31, 2025
58805f0
Add type annotations for param variables
bjsowa Aug 31, 2025
b019861
Use __future__.annotations in every source file
bjsowa Sep 1, 2025
3829388
Set minimal python version to 3.10
bjsowa Sep 1, 2025
86f07fa
Fix assert in message_conversion
bjsowa Sep 1, 2025
69fef70
Remove error messages from asserts in message_conversion
bjsowa Sep 1, 2025
f7a592b
Merge remote-tracking branch 'origin/ros2' into refactor/add-type-ann…
bjsowa Sep 2, 2025
d07fb49
Fix a typo
bjsowa Sep 2, 2025
acd99e1
Merge branch 'ros2' into refactor/add-type-annotations
bjsowa Sep 4, 2025
e0c87c3
Fix shebangs and make all of the tests runnable as scripts
bjsowa Sep 4, 2025
c8bed7e
Use __futute__.annotations in all tests
bjsowa Sep 4, 2025
25fa389
Merge remote-tracking branch 'origin/refactor/add-type-annotations' i…
bjsowa Sep 4, 2025
63d560f
Fix formatting
bjsowa Sep 4, 2025
477f761
Use list_types tuple
bjsowa Sep 6, 2025
1eeae08
Create ROSClock instead of Clock when clock not provided
bjsowa Sep 6, 2025
5bd8947
Fix BSON types
bjsowa Sep 6, 2025
df41bf9
Merge branch 'ros2' into refactor/add-type-annotations
bjsowa Sep 10, 2025
6ed50f2
Use Any type for inst argument in InvalidMessageException
bjsowa Sep 15, 2025
59a8f9d
Raise TypeError instead of AssertionError on type mismatch
bjsowa Sep 16, 2025
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
6 changes: 5 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
[project]
name = "rosbridge_suite"
requires-python = ">=3.10"

[tool.black]
# We use ruff for formatting, but black formatter should be compatible with ruff.
line-length = 100
Expand All @@ -13,7 +17,7 @@ select = [

# flake8 extensions
"YTT", # flake8-2020
# "ANN", # flake8-annotations, There are too many missing annotations right now (TODO)
"ANN", # flake8-annotations,
"ASYNC", # flake8-async
"S", # flake8-bandit
# "BLE", # flake8-blind-except, Too much hassle to fix (TODO)
Expand Down
127 changes: 91 additions & 36 deletions rosapi/scripts/rosapi_node
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

from __future__ import annotations

import os
import sys
from json import dumps, loads
from typing import Any

import rclpy
from rclpy.callback_groups import ReentrantCallbackGroup
Expand Down Expand Up @@ -79,7 +82,7 @@ from rosapi_msgs.srv import (
class Rosapi(Node):
NAME = "rosapi"

def __init__(self):
def __init__(self) -> None:
super().__init__(self.NAME)
self.declare_parameter("topics_glob", "[*]")
self.declare_parameter("services_glob", "[*]")
Expand All @@ -89,7 +92,7 @@ class Rosapi(Node):
self.register_services()

# Initialises the ROS node
def register_services(self):
def register_services(self) -> None:
proxy.init(self)

timeout_sec = self.get_parameter("params_timeout").value
Expand Down Expand Up @@ -187,10 +190,10 @@ class Rosapi(Node):
self.create_service(GetTime, "~/get_time", self.get_time)
self.create_service(GetROSVersion, "~/get_ros_version", self.get_ros_version)

def get_globs(self):
def get_globs(self) -> glob_helper.Globs:
return glob_helper.get_globs(self)

def get_topics(self, _request, response):
def get_topics(self, _request: Topics.Request, response: Topics.Response) -> Topics.Response:
"""
Return a list of all the topics being published.

Expand All @@ -199,7 +202,9 @@ class Rosapi(Node):
response.topics, response.types = proxy.get_topics_and_types(self.globs.topics)
return response

def get_interfaces(self, _request, response):
def get_interfaces(
self, _request: Interfaces.Request, response: Interfaces.Response
) -> Interfaces.Response:
"""
Return a list of all the interfaces in the system.

Expand All @@ -208,7 +213,9 @@ class Rosapi(Node):
response.interfaces = proxy.get_interfaces()
return response

def get_topics_for_type(self, request, response):
def get_topics_for_type(
self, request: TopicsForType.Request, response: TopicsForType.Response
) -> TopicsForType.Response:
"""
Return a list of all the topics that are publishing a given type.

Expand All @@ -217,7 +224,9 @@ class Rosapi(Node):
response.topics = proxy.get_topics_for_type(request.type, self.globs.topics)
return response

def get_topics_and_raw_types(self, _request, response):
def get_topics_and_raw_types(
self, _request: TopicsAndRawTypes.Request, response: TopicsAndRawTypes.Response
) -> TopicsAndRawTypes.Response:
"""
Return a list of all the topics being published, and their raw types.

Expand All @@ -231,7 +240,9 @@ class Rosapi(Node):
]
return response

def get_services(self, _request, response):
def get_services(
self, _request: Services.Request, response: Services.Response
) -> Services.Response:
"""
Return a list of all the services being advertised.

Expand All @@ -240,7 +251,9 @@ class Rosapi(Node):
response.services = proxy.get_services(self.globs.services)
return response

def get_services_for_type(self, request, response):
def get_services_for_type(
self, request: ServicesForType.Request, response: ServicesForType.Response
) -> ServicesForType.Response:
"""
Return a list of all the services that are publishing a given type.

Expand All @@ -249,7 +262,7 @@ class Rosapi(Node):
response.services = proxy.get_services_for_type(request.type, self.globs.services)
return response

def get_nodes(self, _request, response):
def get_nodes(self, _request: Nodes.Request, response: Nodes.Response) -> Nodes.Response:
"""
Return a list of all the nodes that are registered.

Expand All @@ -258,7 +271,9 @@ class Rosapi(Node):
response.nodes = proxy.get_nodes()
return response

def get_node_details(self, request, response):
def get_node_details(
self, request: NodeDetails.Request, response: NodeDetails.Response
) -> NodeDetails.Response:
"""
Return a node description.

Expand All @@ -271,7 +286,9 @@ class Rosapi(Node):
) = proxy.get_node_info(request.node)
return response

def get_action_servers(self, _request, response):
def get_action_servers(
self, _request: GetActionServers.Request, response: GetActionServers.Response
) -> GetActionServers.Response:
"""
Return a list of action servers based on actions standard topics.

Expand All @@ -281,7 +298,9 @@ class Rosapi(Node):
response.action_servers = proxy.filter_action_servers(topics)
return response

def get_topic_type(self, request, response):
def get_topic_type(
self, request: TopicType.Request, response: TopicType.Response
) -> TopicType.Response:
"""
Given the name of a topic, return the name of the type of that topic.

Expand All @@ -294,7 +313,9 @@ class Rosapi(Node):
response.type = proxy.get_topic_type(request.topic, self.globs.topics)
return response

def get_service_type(self, request, response):
def get_service_type(
self, request: ServiceType.Request, response: ServiceType.Response
) -> ServiceType.Response:
"""
Given the name of a service, return the type of that service.

Expand All @@ -307,7 +328,9 @@ class Rosapi(Node):
response.type = proxy.get_service_type(request.service, self.globs.services)
return response

def get_publishers(self, request, response):
def get_publishers(
self, request: Publishers.Request, response: Publishers.Response
) -> Publishers.Response:
"""
Given the name of a topic, return a list of node names that are publishing on that topic.

Expand All @@ -316,7 +339,9 @@ class Rosapi(Node):
response.publishers = proxy.get_publishers(request.topic, self.globs.topics)
return response

def get_subscribers(self, request, response):
def get_subscribers(
self, request: Subscribers.Request, response: Subscribers.Response
) -> Subscribers.Response:
"""
Given the name of a topic, return a list of node names that are subscribing to that topic.

Expand All @@ -325,7 +350,9 @@ class Rosapi(Node):
response.subscribers = proxy.get_subscribers(request.topic, self.globs.topics)
return response

def get_service_providers(self, request, response):
def get_service_providers(
self, request: ServiceProviders.Request, response: ServiceProviders.Response
) -> ServiceProviders.Response:
"""
Given the name of a topic, returns a list of node names that are advertising that service type.

Expand All @@ -334,7 +361,9 @@ class Rosapi(Node):
response.providers = proxy.get_service_providers(request.service, self.globs.services)
return response

def get_service_node(self, request, response):
def get_service_node(
self, request: ServiceNode.Request, response: ServiceNode.Response
) -> ServiceNode.Response:
"""
Given the name of a service, returns the name of the node that is providing that service.

Expand All @@ -343,7 +372,9 @@ class Rosapi(Node):
response.node = proxy.get_service_node(request.service, self.globs.services)
return response

def get_message_details(self, request, response):
def get_message_details(
self, request: MessageDetails.Request, response: MessageDetails.Response
) -> MessageDetails.Response:
"""
Given the name of a message type, return the TypeDef for that type.

Expand All @@ -354,7 +385,9 @@ class Rosapi(Node):
]
return response

def get_service_request_details(self, request, response):
def get_service_request_details(
self, request: ServiceRequestDetails.Request, response: ServiceRequestDetails.Response
) -> ServiceRequestDetails.Response:
"""
Given the name of a service type, return the TypeDef for the request message of that service type.

Expand All @@ -366,7 +399,9 @@ class Rosapi(Node):
]
return response

def get_service_response_details(self, request, response):
def get_service_response_details(
self, request: ServiceResponseDetails.Request, response: ServiceResponseDetails.Response
) -> ServiceResponseDetails.Response:
"""
Given the name of a service type, return the TypeDef for the response message of that service type.

Expand All @@ -378,7 +413,9 @@ class Rosapi(Node):
]
return response

def get_action_goal_details(self, request, response):
def get_action_goal_details(
self, request: ActionGoalDetails.Request, response: ActionGoalDetails.Response
) -> ActionGoalDetails.Response:
"""
Given the name of an action type, return the TypeDef for the goal message of that action type.

Expand All @@ -389,7 +426,9 @@ class Rosapi(Node):
]
return response

def get_action_result_details(self, request, response):
def get_action_result_details(
self, request: ActionResultDetails.Request, response: ActionResultDetails.Response
) -> ActionResultDetails.Response:
"""
Given the name of an action type, return the TypeDef for the result message of that action type.

Expand All @@ -401,7 +440,9 @@ class Rosapi(Node):
]
return response

def get_action_feedback_details(self, request, response):
def get_action_feedback_details(
self, request: ActionFeedbackDetails.Request, response: ActionFeedbackDetails.Response
) -> ActionFeedbackDetails.Response:
"""
Given the name of an action type, return the TypeDef for the feedback message of that action type.

Expand All @@ -413,7 +454,9 @@ class Rosapi(Node):
]
return response

def get_action_type(self, request, response):
def get_action_type(
self, request: ActionType.Request, response: ActionType.Response
) -> ActionType.Response:
"""
Given the name of an action, return its type.

Expand All @@ -424,7 +467,9 @@ class Rosapi(Node):
response.type = proxy.get_action_type(request.action)
return response

async def set_param(self, request, response):
async def set_param(
self, request: SetParam.Request, response: SetParam.Response
) -> SetParam.Response:
try:
node_name, param_name = self._get_node_and_param_name(request.name)
except ValueError:
Expand All @@ -440,7 +485,9 @@ class Rosapi(Node):

return response

async def get_param(self, request, response):
async def get_param(
self, request: GetParam.Request, response: GetParam.Response
) -> GetParam.Response:
try:
node_name, param_name = self._get_node_and_param_name(request.name)
except ValueError:
Expand All @@ -467,7 +514,9 @@ class Rosapi(Node):

return response

async def has_param(self, request, response):
async def has_param(
self, request: HasParam.Request, response: HasParam.Response
) -> HasParam.Response:
try:
node_name, param_name = self._get_node_and_param_name(request.name)
except ValueError:
Expand All @@ -477,7 +526,9 @@ class Rosapi(Node):

return response

async def delete_param(self, request, response):
async def delete_param(
self, request: DeleteParam.Request, response: DeleteParam.Response
) -> DeleteParam.Response:
try:
node_name, param_name = self._get_node_and_param_name(request.name)
except ValueError:
Expand All @@ -493,29 +544,33 @@ class Rosapi(Node):

return response

async def get_param_names(self, _request, response):
async def get_param_names(
self, _request: GetParamNames.Request, response: GetParamNames.Response
) -> GetParamNames.Response:
response.names = await params.get_param_names(self.globs.params)
return response

def get_time(self, _request, response):
def get_time(self, _request: GetTime.Request, response: GetTime.Response) -> GetTime.Response:
response.time = Clock(clock_type=ClockType.ROS_TIME).now().to_msg()
return response

def _get_node_and_param_name(self, param):
def _get_node_and_param_name(self, param: str) -> tuple[str, str]:
return tuple(param.split(":"))

def _print_malformed_param_name_warning(self, param_name):
def _print_malformed_param_name_warning(self, param_name: str) -> None:
self.get_logger().warning(
f"Malformed parameter name: {param_name}; expecting <node_name>:<param_name>"
)

def get_ros_version(self, _request, response):
def get_ros_version(
self, _request: GetROSVersion.Request, response: GetROSVersion.Response
) -> GetROSVersion.Response:
response.version = 2
response.distro = str(os.environ["ROS_DISTRO"])
return response


def dict_to_typedef(typedefdict):
def dict_to_typedef(typedefdict: dict[str, Any]) -> TypeDef:
typedef = TypeDef()
typedef.type = typedefdict["type"]
typedef.fieldnames = typedefdict["fieldnames"]
Expand All @@ -527,7 +582,7 @@ def dict_to_typedef(typedefdict):
return typedef


def main(args=None):
def main(args: list[str] | None = None) -> None:
if args is None:
args = sys.argv

Expand Down
Loading