diff --git a/rclpy/rclpy/client.py b/rclpy/rclpy/client.py index 99bdce9b3..b5d53258a 100644 --- a/rclpy/rclpy/client.py +++ b/rclpy/rclpy/client.py @@ -16,6 +16,7 @@ import time from types import TracebackType from typing import Dict +from typing import Generic from typing import Optional from typing import Type from typing import TypeVar @@ -27,19 +28,20 @@ from rclpy.qos import QoSProfile from rclpy.service_introspection import ServiceIntrospectionState from rclpy.task import Future +from rclpy.type_support import Srv, SrvEventT, SrvRequestT, SrvResponseT -# Used for documentation purposes only +# Left To Support Legacy TypeVars SrvType = TypeVar('SrvType') SrvTypeRequest = TypeVar('SrvTypeRequest') SrvTypeResponse = TypeVar('SrvTypeResponse') -class Client: +class Client(Generic[SrvRequestT, SrvResponseT, SrvEventT]): def __init__( self, context: Context, client_impl: _rclpy.Client, - srv_type: SrvType, + srv_type: Srv[SrvRequestT, SrvResponseT, SrvEventT], srv_name: str, qos_profile: QoSProfile, callback_group: CallbackGroup @@ -73,9 +75,9 @@ def __init__( def call( self, - request: SrvTypeRequest, + request: SrvRequestT, timeout_sec: Optional[float] = None - ) -> Optional[SrvTypeResponse]: + ) -> Optional[SrvResponseT]: """ Make a service request and wait for the result. @@ -111,7 +113,7 @@ def unblock(future): raise future.exception() return future.result() - def call_async(self, request: SrvTypeRequest) -> Future: + def call_async(self, request: SrvRequestT) -> Future: """ Make a service request and asynchronously get the result. diff --git a/rclpy/rclpy/node.py b/rclpy/rclpy/node.py index 873b8df81..a70f1d7c3 100644 --- a/rclpy/rclpy/node.py +++ b/rclpy/rclpy/node.py @@ -85,6 +85,10 @@ from rclpy.type_support import check_is_valid_msg_type from rclpy.type_support import check_is_valid_srv_type from rclpy.type_support import MsgT +from rclpy.type_support import Srv +from rclpy.type_support import SrvEventT +from rclpy.type_support import SrvRequestT +from rclpy.type_support import SrvResponseT from rclpy.utilities import get_default_context from rclpy.validate_full_topic_name import validate_full_topic_name from rclpy.validate_namespace import validate_namespace @@ -1673,12 +1677,12 @@ def create_subscription( def create_client( self, - srv_type, + srv_type: Srv[SrvRequestT, SrvResponseT, SrvEventT], srv_name: str, *, qos_profile: QoSProfile = qos_profile_services_default, callback_group: Optional[CallbackGroup] = None - ) -> Client: + ) -> Client[SrvRequestT, SrvResponseT, SrvEventT]: """ Create a new service client. @@ -1715,13 +1719,13 @@ def create_client( def create_service( self, - srv_type, + srv_type: Srv[SrvRequestT, SrvResponseT, SrvEventT], srv_name: str, - callback: Callable[[SrvTypeRequest, SrvTypeResponse], SrvTypeResponse], + callback: Callable[[SrvRequestT, SrvResponseT], SrvResponseT], *, qos_profile: QoSProfile = qos_profile_services_default, callback_group: Optional[CallbackGroup] = None - ) -> Service: + ) -> Service[SrvRequestT, SrvResponseT, SrvEventT]: """ Create a new service server. diff --git a/rclpy/rclpy/service.py b/rclpy/rclpy/service.py index 7687530fd..78a004dac 100644 --- a/rclpy/rclpy/service.py +++ b/rclpy/rclpy/service.py @@ -14,6 +14,7 @@ from types import TracebackType from typing import Callable +from typing import Generic from typing import Optional from typing import Type from typing import TypeVar @@ -23,6 +24,7 @@ from rclpy.impl.implementation_singleton import rclpy_implementation as _rclpy from rclpy.qos import QoSProfile from rclpy.service_introspection import ServiceIntrospectionState +from rclpy.type_support import Srv, SrvEventT, SrvRequestT, SrvResponseT # Used for documentation purposes only SrvType = TypeVar('SrvType') @@ -30,13 +32,13 @@ SrvTypeResponse = TypeVar('SrvTypeResponse') -class Service: +class Service(Generic[SrvRequestT, SrvResponseT, SrvEventT]): def __init__( self, service_impl: _rclpy.Service, - srv_type: SrvType, + srv_type: Srv[SrvRequestT, SrvResponseT, SrvEventT], srv_name: str, - callback: Callable[[SrvTypeRequest, SrvTypeResponse], SrvTypeResponse], + callback: Callable[[SrvRequestT, SrvResponseT], SrvResponseT], callback_group: CallbackGroup, qos_profile: QoSProfile ) -> None: @@ -64,7 +66,7 @@ def __init__( self._executor_event = False self.qos_profile = qos_profile - def send_response(self, response: SrvTypeResponse, header) -> None: + def send_response(self, response: SrvResponseT, header) -> None: """ Send a service response. diff --git a/rclpy/rclpy/type_support.py b/rclpy/rclpy/type_support.py index 29a4e1ae2..a06891127 100644 --- a/rclpy/rclpy/type_support.py +++ b/rclpy/rclpy/type_support.py @@ -12,7 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import Optional, Protocol, Type, TypeVar, Union +from typing import NoReturn, Optional, Protocol, Type, TypeVar, Union from rclpy.exceptions import NoTypeSupportImportedException @@ -47,26 +47,33 @@ class MsgMetaClass(CommonMsgSrvMetaClass): class Msg(Protocol, metaclass=MsgMetaClass): - """Generic Message Type Alias.""" - - pass - - -# Could likely be improved if generic across Request, Response, Event -class Srv(Protocol, metaclass=CommonMsgSrvMetaClass): - """Generic Service Type Alias.""" + """Generic Message Alias.""" pass MsgT = TypeVar('MsgT', bound=Msg) -SrvT = TypeVar('SrvT', bound=Srv) SrvRequestT = TypeVar('SrvRequestT', bound=Msg) SrvResponseT = TypeVar('SrvResponseT', bound=Msg) SrvEventT = TypeVar('SrvEventT', bound=Msg) +class Srv(Protocol[SrvRequestT, SrvResponseT, SrvEventT], metaclass=CommonMsgSrvMetaClass): + """Generic Service Type Alias.""" + + Request: Type[SrvRequestT] + Response: Type[SrvResponseT] + Event: Type[SrvEventT] + + def __init__(self) -> NoReturn: + ... + + +# Can be used if https://github.com/python/typing/issues/548 ever gets approved. +SrvT = TypeVar('SrvT', bound=Type[Srv]) + + def check_for_type_support(msg_or_srv_type: Type[Union[Msg, Srv]]) -> None: try: ts = msg_or_srv_type._TYPE_SUPPORT