diff --git a/rclpy/rclpy/node.py b/rclpy/rclpy/node.py index c95115e0c..5a693e078 100644 --- a/rclpy/rclpy/node.py +++ b/rclpy/rclpy/node.py @@ -34,6 +34,7 @@ from rcl_interfaces.msg import ParameterEvent from rcl_interfaces.msg import ParameterValue from rcl_interfaces.msg import SetParametersResult + from rclpy.callback_groups import CallbackGroup from rclpy.callback_groups import MutuallyExclusiveCallbackGroup from rclpy.client import Client @@ -83,6 +84,10 @@ SrvTypeRequest = TypeVar('SrvTypeRequest') SrvTypeResponse = TypeVar('SrvTypeResponse') +# Re-export exception defined in _rclpy C extension. +# `Node.get_*_names_and_types_by_node` methods may raise this error. +NodeNameNonExistentError = _rclpy.NodeNameNonExistentError + class Node: @@ -1463,6 +1468,8 @@ def get_publisher_names_and_types_by_node( :return: List of tuples. The first element of each tuple is the topic name and the second element is a list of topic types. + :raise NodeNameNonExistentError: If the node wasn't found. + :raise RuntimeError: Unexpected failure. """ with self.handle as capsule: return _rclpy.rclpy_get_publisher_names_and_types_by_node( @@ -1483,6 +1490,8 @@ def get_subscriber_names_and_types_by_node( :return: List of tuples. The first element of each tuple is the topic name and the second element is a list of topic types. + :raise NodeNameNonExistentError: If the node wasn't found. + :raise RuntimeError: Unexpected failure. """ with self.handle as capsule: return _rclpy.rclpy_get_subscriber_names_and_types_by_node( @@ -1501,6 +1510,8 @@ def get_service_names_and_types_by_node( :return: List of tuples. The first element of each tuple is the service server name and the second element is a list of service types. + :raise NodeNameNonExistentError: If the node wasn't found. + :raise RuntimeError: Unexpected failure. """ with self.handle as capsule: return _rclpy.rclpy_get_service_names_and_types_by_node( @@ -1519,6 +1530,8 @@ def get_client_names_and_types_by_node( :return: List of tuples. The fist element of each tuple is the service client name and the second element is a list of service client types. + :raise NodeNameNonExistentError: If the node wasn't found. + :raise RuntimeError: Unexpected failure. """ with self.handle as capsule: return _rclpy.rclpy_get_client_names_and_types_by_node( diff --git a/rclpy/src/rclpy/_rclpy.c b/rclpy/src/rclpy/_rclpy.c index dca227364..d096bd8de 100644 --- a/rclpy/src/rclpy/_rclpy.c +++ b/rclpy/src/rclpy/_rclpy.c @@ -41,6 +41,7 @@ static PyObject * RCLInvalidROSArgsError; static PyObject * UnknownROSArgsError; +static PyObject * NodeNameNonExistentError; void _rclpy_context_capsule_destructor(PyObject * capsule) @@ -3213,10 +3214,13 @@ rclpy_get_service_names_and_types_by_node(PyObject * Py_UNUSED(self), PyObject * rcl_get_service_names_and_types_by_node(node, &allocator, node_name, node_namespace, &service_names_and_types); if (ret != RCL_RET_OK) { - PyErr_Format(PyExc_RuntimeError, + PyObject * error = PyExc_RuntimeError; + if (ret == RCL_RET_NODE_NAME_NON_EXISTENT) { + error = NodeNameNonExistentError; + } + PyErr_Format(error, "Failed to get_service_names_and_types: %s", rcl_get_error_string().str); rcl_reset_error(); - rclpy_names_and_types_fini(&service_names_and_types); return NULL; } @@ -3262,10 +3266,13 @@ rclpy_get_client_names_and_types_by_node(PyObject * Py_UNUSED(self), PyObject * rcl_get_client_names_and_types_by_node(node, &allocator, node_name, node_namespace, &client_names_and_types); if (ret != RCL_RET_OK) { - PyErr_Format(PyExc_RuntimeError, + PyObject * error = PyExc_RuntimeError; + if (ret == RCL_RET_NODE_NAME_NON_EXISTENT) { + error = NodeNameNonExistentError; + } + PyErr_Format(error, "Failed to get_client_names_and_types: %s", rcl_get_error_string().str); rcl_reset_error(); - rclpy_names_and_types_fini(&client_names_and_types); return NULL; } @@ -3313,7 +3320,11 @@ rclpy_get_subscriber_names_and_types_by_node(PyObject * Py_UNUSED(self), PyObjec rcl_get_subscriber_names_and_types_by_node(node, &allocator, no_demangle, node_name, node_namespace, &topic_names_and_types); if (ret != RCL_RET_OK) { - PyErr_Format(PyExc_RuntimeError, + PyObject * error = PyExc_RuntimeError; + if (ret == RCL_RET_NODE_NAME_NON_EXISTENT) { + error = NodeNameNonExistentError; + } + PyErr_Format(error, "Failed to get_subscriber_names_and_types: %s", rcl_get_error_string().str); rcl_reset_error(); return NULL; @@ -3362,7 +3373,11 @@ rclpy_get_publisher_names_and_types_by_node(PyObject * Py_UNUSED(self), PyObject rcl_get_publisher_names_and_types_by_node(node, &allocator, no_demangle, node_name, node_namespace, &topic_names_and_types); if (ret != RCL_RET_OK) { - PyErr_Format(PyExc_RuntimeError, + PyObject * error = PyExc_RuntimeError; + if (ret == RCL_RET_NODE_NAME_NON_EXISTENT) { + error = NodeNameNonExistentError; + } + PyErr_Format(error, "Failed to get_publisher_names_and_types: %s", rcl_get_error_string().str); rcl_reset_error(); return NULL; @@ -5081,6 +5096,15 @@ PyMODINIT_FUNC PyInit__rclpy(void) return NULL; } + NodeNameNonExistentError = PyErr_NewExceptionWithDoc( + "_rclpy.NodeNameNonExistentError", + "Thrown when a node name is not found.", + PyExc_RuntimeError, NULL); + if (PyModule_AddObject(m, "NodeNameNonExistentError", NodeNameNonExistentError)) { + Py_DECREF(m); + return NULL; + } + if (PyErr_Occurred()) { Py_DECREF(m); return NULL;