From 6d0e854ee143ca445964bc9843737205e1a32d66 Mon Sep 17 00:00:00 2001 From: Jonas Otto Date: Fri, 25 Aug 2023 19:24:29 +0000 Subject: [PATCH] type checking etc --- .vscode/settings.json | 9 ++--- ros2/orchestrator/orchestrator/lib.py | 2 + .../orchestrator_lib/node_model.py | 2 +- .../orchestrator_lib/node_model_from_file.py | 33 +++++++++-------- .../orchestrator_lib/orchestrator.py | 6 +-- .../orchestrator_lib/remapping_generation.py | 10 +++-- .../orchestrator_lib/ros_utils/logger.py | 6 ++- .../ros_utils/message_filter.py | 4 +- .../orchestrator_lib/ros_utils/pubsub.py | 12 +++--- .../orchestrator_lib/ros_utils/spin.py | 2 + ros2/orchestrator/pyrightconfig.json | 4 +- .../typings/deepdiff/__init__.pyi | 8 ++++ ros2/orchestrator/typings/deepdiff/diff.pyi | 8 ++++ .../typings/message_filters/__init__.pyi | 37 +++++++++++++++++++ .../typings/rclpy/serialization.pyi | 13 +++++++ 15 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 ros2/orchestrator/typings/deepdiff/__init__.pyi create mode 100644 ros2/orchestrator/typings/deepdiff/diff.pyi create mode 100644 ros2/orchestrator/typings/message_filters/__init__.pyi create mode 100644 ros2/orchestrator/typings/rclpy/serialization.pyi diff --git a/.vscode/settings.json b/.vscode/settings.json index e5c1066..6b682e7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,4 @@ { - "python.formatting.autopep8Args": [ - "--max-line-length=150" - ], "python.autoComplete.extraPaths": [ "/home/gja38/sandbox_otto/install/orchestrator_interfaces/local/lib/python3.10/dist-packages", "/opt/carolo/install/spatz_interfaces/local/lib/python3.10/dist-packages", @@ -18,7 +15,6 @@ "/home/gja38/sandbox_otto/src/orchestrator", "/workspaces/ros2_def/install/orchestrator_interfaces/lib/python3.10/site-packages" ], - "python.analysis.typeCheckingMode": "basic", "python.analysis.inlayHints.functionReturnTypes": true, "python.analysis.inlayHints.variableTypes": true, "files.exclude": { @@ -47,5 +43,8 @@ "editor.stickyScroll.enabled": true, "terminal.integrated.scrollback": 10000, "python.analysis.stubPath": "ros2/orchestrator/typings", - "cmake.configureOnOpen": false + "cmake.configureOnOpen": false, + "[python]": { + "editor.defaultFormatter": "ms-python.autopep8" + } } \ No newline at end of file diff --git a/ros2/orchestrator/orchestrator/lib.py b/ros2/orchestrator/orchestrator/lib.py index ca801f1..fc6e81d 100644 --- a/ros2/orchestrator/orchestrator/lib.py +++ b/ros2/orchestrator/orchestrator/lib.py @@ -1 +1,3 @@ from orchestrator.orchestrator_lib.orchestrator import Orchestrator + +__all__ = ["Orchestrator"] diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/node_model.py b/ros2/orchestrator/orchestrator/orchestrator_lib/node_model.py index 41c5325..de08ce9 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/node_model.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/node_model.py @@ -57,7 +57,7 @@ def __init__(self, name: str, remappings: SimpleRemapRules) -> None: super().__init__() @abstractmethod - def state_sequence_push(self, x: Any): + def state_sequence_push(self, message: Any): ... @abstractmethod diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/node_model_from_file.py b/ros2/orchestrator/orchestrator/orchestrator_lib/node_model_from_file.py index 07d0d18..ef73f15 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/node_model_from_file.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/node_model_from_file.py @@ -1,9 +1,11 @@ +# pyright: strict + from __future__ import annotations import base64 import json from dataclasses import dataclass -from typing import cast, final, Dict, Optional, Any +from typing import Iterable, cast, final, Optional, Any from orchestrator.orchestrator_lib.name_utils import normalize_topic_name from orchestrator.orchestrator_lib.node_model import Cause, Effect, NodeModel, ServiceCall, ServiceName, \ @@ -41,23 +43,23 @@ def dump_state_sequence(self): with open('state_sequence_' + self.get_name() + '.json', 'w') as f: json.dump(self.state_recording, f) - def __init__(self, node_config: dict, name, remappings: Dict[str, str], - state_sequence: Optional[list] = None) -> None: + def __init__(self, node_config: dict[str, Any], name: str, remappings: dict[str, str], + state_sequence: Optional[list[str]] = None) -> None: - self.state_sequence = state_sequence + self.state_sequence: Optional[list[str]] = state_sequence if self.state_sequence is not None: self.state_sequence.reverse() - self.state_recording = [] + self.state_recording: list[str] = [] # Mappings from internal to external name mappings: dict[str, str] = {} - inputs = set() + inputs: set[str] = set() # Initialize mappings by identity for all known inputs and outputs from # node config. - for callback in node_config["callbacks"]: - trigger = callback["trigger"] + for callback in cast(Iterable[dict[str, Any]], node_config["callbacks"]): + trigger: str | dict[str, Any] = cast(str | dict[str, Any], callback["trigger"]) if isinstance(trigger, str): trigger = normalize_topic_name(trigger) @@ -114,7 +116,7 @@ def __init__(self, node_config: dict, name, remappings: Dict[str, str], # Mapping from external topic input to external topic outputs self.effects: dict[Cause, Callback] = {} - def add_effect(trigger: Cause, outputs, service_calls, changes_dp_state: bool, may_reconfigure: bool): + def add_effect(trigger: Cause, outputs: Iterable[str], service_calls: Iterable[str], changes_dp_state: bool, may_reconfigure: bool): output_effects: list[Effect] = [] for output in outputs: output = normalize_topic_name(output) @@ -136,19 +138,19 @@ def add_effect(trigger: Cause, outputs, service_calls, changes_dp_state: bool, m for callback in node_config["callbacks"]: trigger = callback["trigger"] - + cause: Cause if isinstance(trigger, str): trigger = normalize_topic_name(trigger) - trigger = self.internal_topic_input(trigger) - add_effect(trigger, + cause = self.internal_topic_input(trigger) + add_effect(cause, callback.get("outputs", []), callback.get("service_calls", []), callback.get("changes_dataprovider_state", False), callback.get("may_cause_reconfiguration", False)) elif trigger.get("type", None) == "topic" and "name" in trigger: topic_name = normalize_topic_name(trigger["name"]) - trigger = self.internal_topic_input(topic_name) - add_effect(trigger, + cause = self.internal_topic_input(topic_name) + add_effect(cause, callback.get("outputs", []), callback.get("service_calls", []), callback.get("changes_dataprovider_state", False), @@ -164,8 +166,7 @@ def add_effect(trigger: Cause, outputs, service_calls, changes_dp_state: bool, m callback.get("service_calls", []), callback.get("changes_dataprovider_state", False), callback.get("may_cause_reconfiguration", False)) - elif trigger.get("type", - None) == "approximate_time_sync" and "input_topics" in trigger and "slop" in trigger and "queue_size" in trigger: + elif trigger.get("type", None) == "approximate_time_sync" and "input_topics" in trigger and "slop" in trigger and "queue_size" in trigger: input_topics = trigger["input_topics"] slop = trigger["slop"] queue = trigger["queue_size"] diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/orchestrator.py b/ros2/orchestrator/orchestrator/orchestrator_lib/orchestrator.py index f30b4aa..3276398 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/orchestrator.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/orchestrator.py @@ -1,4 +1,4 @@ -# at some point, enable: pyright: strict, reportMissingTypeStubs=true +# pyright: basic import datetime from dataclasses import dataclass @@ -235,7 +235,7 @@ def intercept_topic(canonical_name: TopicName, node: NodeModel): TopicType, effect.output_topic, lambda msg, - topic_name=effect.output_topic: self.__interception_subscription_callback( + topic_name=effect.output_topic: self.__interception_subscription_callback( topic_name, msg), 10, raw=(TopicType != rosgraph_msgs.msg.Clock)) self.interception_subs[effect.output_topic] = sub @@ -648,7 +648,7 @@ def __buffer_nodes_with_data(self) -> Generator[Tuple[GraphNodeId, OrchestratorB yield (id, action) def __buffer_childs_of_parent(self, parent: GraphNodeId) -> Generator[ - Tuple[GraphNodeId, OrchestratorBufferAction], None, None]: + Tuple[GraphNodeId, OrchestratorBufferAction], None, None]: for id, data in self.__buffer_nodes_with_data(): if self.graph.has_edge(id, parent): yield id, data diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/remapping_generation.py b/ros2/orchestrator/orchestrator/orchestrator_lib/remapping_generation.py index 3f9e45a..c19eb18 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/remapping_generation.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/remapping_generation.py @@ -1,3 +1,5 @@ +# pyright: strict + import sys from typing import List, Dict @@ -6,8 +8,8 @@ from orchestrator.orchestrator_lib.name_utils import intercepted_name, normalize_topic_name from .model_loader import * -from launch_ros.actions import SetRemap -from launch.substitutions import TextSubstitution +from launch_ros.actions import SetRemap # pyright: ignore [reportMissingTypeStubs] +from launch.substitutions import TextSubstitution # pyright: ignore [reportMissingTypeStubs] def _find_node_model(name: str, models: List[NodeModel]) -> NodeModel: @@ -38,7 +40,7 @@ def generate_remappings_from_config_file(package_name: str, launch_config_file: return generate_remappings_from_config(launch_config) -def generate_remappings_from_config(launch_config: dict) -> List[SetRemap]: +def generate_remappings_from_config(launch_config: dict[str, Any]) -> List[SetRemap]: """ Generate remappings for topic interception by orchestrator. @@ -49,7 +51,7 @@ def generate_remappings_from_config(launch_config: dict) -> List[SetRemap]: """ node_models = load_models(launch_config, load_node_config_schema()) - remap_actions = [] + remap_actions: list[SetRemap] = [] for node_name, node in launch_config["nodes"].items(): if "/" in node_name: diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/logger.py b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/logger.py index 35e441f..211b505 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/logger.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/logger.py @@ -1,2 +1,6 @@ -def lc(logger, msg): +# pyright: strict +from rclpy.impl.rcutils_logger import RcutilsLogger + + +def lc(logger: RcutilsLogger, msg: str) -> bool: return logger.info('\033[96m' + msg + '\033[0m') diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/message_filter.py b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/message_filter.py index 6ac61da..43afd64 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/message_filter.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/message_filter.py @@ -1,3 +1,5 @@ +# pyright: strict + from typing import Any, List, Mapping from threading import Lock from message_filters import SimpleFilter, ApproximateTimeSynchronizer @@ -19,7 +21,7 @@ def __init__(self, topic_names: List[str], queue_size: int, slop: float) -> None self._lock = Lock() self._time_synchronizer.registerCallback(self.__callback) - def __callback(self, *_msgs): + def __callback(self, *_msgs: Any): self._was_called = True def test_input(self, topic_name: str, msg: Any) -> bool: diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/pubsub.py b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/pubsub.py index 2263e9d..5ba81ff 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/pubsub.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/pubsub.py @@ -1,4 +1,6 @@ -from typing import Type, Optional +# pyright: strict + +from typing import Any, Type, Optional from rclpy.task import Future from rclpy.executors import Executor @@ -8,7 +10,7 @@ from orchestrator.orchestrator_lib.name_utils import TopicName, type_from_string -def wait_for_topic(name: TopicName, logger: RcutilsLogger, node: Node, executor: Executor) -> Type: +def wait_for_topic(name: TopicName, logger: RcutilsLogger, node: Node, executor: Executor) -> Type[Any]: name = node.resolve_topic_name(name) def find_type(): @@ -29,10 +31,10 @@ def find_type(): return msgtype -def wait_for_node_sub(topic_name: str, node_name: str, logger: RcutilsLogger, node: Node, executor: Executor) -> Type: +def wait_for_node_sub(topic_name: str, node_name: str, logger: RcutilsLogger, node: Node, executor: Executor) -> Type[Any]: topic_name = node.resolve_topic_name(topic_name) - def try_get_type() -> Optional[Type]: + def try_get_type() -> Optional[Type[Any]]: for info in node.get_subscriptions_info_by_topic(topic_name): if info.node_name == node_name: return type_from_string(info.topic_type) @@ -59,7 +61,7 @@ def wait_for_node_pub(topic_name: str, node_name: str, logger: RcutilsLogger, no def node_has_pub(): by_node = node.get_publisher_names_and_types_by_node(node_name, node.get_namespace()) - for topic, types in by_node: + for topic, _types in by_node: if topic == topic_name: return True return False diff --git a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/spin.py b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/spin.py index b2875d5..6a676d1 100644 --- a/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/spin.py +++ b/ros2/orchestrator/orchestrator/orchestrator_lib/ros_utils/spin.py @@ -1,3 +1,5 @@ +# pyright: strict + import datetime from rclpy.executors import Executor diff --git a/ros2/orchestrator/pyrightconfig.json b/ros2/orchestrator/pyrightconfig.json index d9cb77b..0a74f33 100644 --- a/ros2/orchestrator/pyrightconfig.json +++ b/ros2/orchestrator/pyrightconfig.json @@ -1,5 +1,7 @@ { "include": [ "orchestrator" - ] + ], + "typeCheckingMode": "strict", + "reportUnnecessaryTypeIgnoreComment": "error" } \ No newline at end of file diff --git a/ros2/orchestrator/typings/deepdiff/__init__.pyi b/ros2/orchestrator/typings/deepdiff/__init__.pyi new file mode 100644 index 0000000..d373734 --- /dev/null +++ b/ros2/orchestrator/typings/deepdiff/__init__.pyi @@ -0,0 +1,8 @@ +""" +This type stub file was generated by pyright. +""" + +import logging +from .diff import DeepDiff + +__all__ = ["DeepDiff"] diff --git a/ros2/orchestrator/typings/deepdiff/diff.pyi b/ros2/orchestrator/typings/deepdiff/diff.pyi new file mode 100644 index 0000000..baecc71 --- /dev/null +++ b/ros2/orchestrator/typings/deepdiff/diff.pyi @@ -0,0 +1,8 @@ +""" +This type stub file was generated by pyright. +""" + + +class DeepDiff: + def __init__(self, t1, t2) -> None: + ... diff --git a/ros2/orchestrator/typings/message_filters/__init__.pyi b/ros2/orchestrator/typings/message_filters/__init__.pyi new file mode 100644 index 0000000..8995d8e --- /dev/null +++ b/ros2/orchestrator/typings/message_filters/__init__.pyi @@ -0,0 +1,37 @@ +""" +This type stub file was generated by pyright. +""" + + +from typing import Any, Callable, Iterable + + +class SimpleFilter: + def __init__(self) -> None: + ... + + def registerCallback(self, cb: Callable[..., None], *args: Any) -> int: + ... + + def signalMessage(self, *msg: Any) -> None: + ... + + +class TimeSynchronizer(SimpleFilter): + def __init__(self, fs, queue_size) -> None: + ... + + def connectInput(self, fs): # -> None: + ... + + def add(self, msg, my_queue, my_queue_index=...): # -> None: + ... + + +class ApproximateTimeSynchronizer(TimeSynchronizer): + + def __init__(self, fs: Iterable[SimpleFilter], queue_size, slop, allow_headerless=...) -> None: + ... + + def add(self, msg, my_queue, my_queue_index=...): # -> None: + ... diff --git a/ros2/orchestrator/typings/rclpy/serialization.pyi b/ros2/orchestrator/typings/rclpy/serialization.pyi new file mode 100644 index 0000000..d100528 --- /dev/null +++ b/ros2/orchestrator/typings/rclpy/serialization.pyi @@ -0,0 +1,13 @@ +""" +This type stub file was generated by pyright. +""" + +from typing import Any, Type + + +def serialize_message(message: Any) -> bytes: + ... + + +def deserialize_message(serialized_message: bytes, message_type: Type[Any]) -> Any: + ...