Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
10 changes: 10 additions & 0 deletions rclpy/rclpy/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -1644,6 +1644,16 @@ def get_node_names_and_namespaces(self) -> List[Tuple[str, str]]:
with self.handle as capsule:
return _rclpy.rclpy_get_node_names_and_namespaces(capsule)

def get_node_names_and_namespaces_with_enclaves(self) -> List[Tuple[str, str, str]]:
"""
Get a list of names, namespaces and enclaves for discovered nodes.

:return: List of tuples containing three strings: the node name, node namespace
and enclave.
"""
with self.handle as capsule:
return _rclpy.rclpy_get_node_names_and_namespaces_with_enclaves(capsule)

def _count_publishers_or_subscribers(self, topic_name, func):
fq_topic_name = expand_topic_name(topic_name, self.get_name(), self.get_namespace())
validate_full_topic_name(fq_topic_name)
Expand Down
82 changes: 75 additions & 7 deletions rclpy/src/rclpy/_rclpy.c
Original file line number Diff line number Diff line change
Expand Up @@ -3428,12 +3428,15 @@ rclpy_shutdown(PyObject * Py_UNUSED(self), PyObject * args)
* Raises ValueError if pynode is not a node capsule
* Raises RuntimeError if there is an rcl error
*
* \param[in] pynode Capsule pointing to the node
* \return Python list of tuples where each tuple contains the two strings:
* the node name and node namespace
* \param[in] args arguments tuple, composed by only one argument:
* - node: Capsule pointing to the node
* \param[in] get_enclaves specifies if the output includes the enclaves names or not
* \return Python list of tuples, containing:
* node name, node namespace, and
* enclave if `get_enclaves` is true.
*/
static PyObject *
rclpy_get_node_names_and_namespaces(PyObject * Py_UNUSED(self), PyObject * args)
rclpy_get_node_names_impl(PyObject * args, bool get_enclaves)
{
PyObject * pynode;

Expand All @@ -3448,23 +3451,33 @@ rclpy_get_node_names_and_namespaces(PyObject * Py_UNUSED(self), PyObject * args)
}
rcutils_string_array_t node_names = rcutils_get_zero_initialized_string_array();
rcutils_string_array_t node_namespaces = rcutils_get_zero_initialized_string_array();
rcl_ret_t ret = rcl_get_node_names(node, allocator, &node_names, &node_namespaces);
rcutils_string_array_t enclaves = rcutils_get_zero_initialized_string_array();
rcl_ret_t ret = RCL_RET_OK;
if (get_enclaves) {
ret = rcl_get_node_names_with_enclaves(
node, allocator, &node_names, &node_namespaces, &enclaves);
} else {
ret = rcl_get_node_names(
node, allocator, &node_names, &node_namespaces);
}
if (ret != RCL_RET_OK) {
PyErr_Format(
RCLError, "Failed to get_node_names: %s", rcl_get_error_string().str);
RCLError, "Failed to get node names: %s", rcl_get_error_string().str);
rcl_reset_error();
return NULL;
}

rcutils_ret_t fini_names_ret;
rcutils_ret_t fini_namespaces_ret;
rcutils_ret_t fini_enclaves_ret;
PyObject * pynode_names_and_namespaces = PyList_New(node_names.size);
if (!pynode_names_and_namespaces) {
goto cleanup;
}

size_t idx;
for (idx = 0; idx < node_names.size; ++idx) {
PyObject * pytuple = PyTuple_New(2);
PyObject * pytuple = PyTuple_New(get_enclaves ? 3 : 2);
if (!pytuple) {
goto cleanup;
}
Expand All @@ -3482,13 +3495,23 @@ rclpy_get_node_names_and_namespaces(PyObject * Py_UNUSED(self), PyObject * args)
}
// Steals the reference
PyTuple_SET_ITEM(pytuple, 1, pynode_namespace);
if (get_enclaves) {
PyObject * pynode_enclaves = PyUnicode_FromString(enclaves.data[idx]);
if (!pynode_enclaves) {
Py_DECREF(pytuple);
goto cleanup;
}
// Steals the reference
PyTuple_SET_ITEM(pytuple, 2, pynode_enclaves);
}
// Steals the reference
PyList_SET_ITEM(pynode_names_and_namespaces, idx, pytuple);
}

cleanup:
fini_names_ret = rcutils_string_array_fini(&node_names);
fini_namespaces_ret = rcutils_string_array_fini(&node_namespaces);
fini_enclaves_ret = rcutils_string_array_fini(&enclaves);
if (PyErr_Occurred()) {
Py_XDECREF(pynode_names_and_namespaces);
return NULL;
Expand All @@ -3509,10 +3532,49 @@ rclpy_get_node_names_and_namespaces(PyObject * Py_UNUSED(self), PyObject * args)
rcl_reset_error();
return NULL;
}
if (fini_enclaves_ret != RCUTILS_RET_OK) {
PyErr_Format(
RCLError,
"Failed to destroy enclaves string array: %s", rcl_get_error_string().str);
Py_DECREF(pynode_names_and_namespaces);
rcl_reset_error();
return NULL;
}

return pynode_names_and_namespaces;
}

/// Get the list of nodes discovered by the provided node
/**
* Raises ValueError if pynode is not a node capsule
* Raises RuntimeError if there is an rcl error
*
* \param[in] pynode Capsule pointing to the node
* \return Python list of tuples where each tuple contains the two strings:
* the node name and node namespace
*/
static PyObject *
rclpy_get_node_names_and_namespaces(PyObject * Py_UNUSED(self), PyObject * args)
{
return rclpy_get_node_names_impl(args, false);
}

/// Get the list of nodes discovered by the provided node, with their respective enclaves.
/**
* Raises ValueError if pynode is not a node capsule
* Raises RuntimeError if there is an rcl error
*
* \param[in] pynode Capsule pointing to the node
* \return Python list of tuples where each tuple contains three strings:
* node name, node namespace, and enclave.
*/
static PyObject *
rclpy_get_node_names_and_namespaces_with_enclaves(
PyObject * Py_UNUSED(self), PyObject * args)
{
return rclpy_get_node_names_impl(args, true);
}

/// Get a list of service names and types associated with the given node name.
/**
* Raises ValueError if pynode is not a node capsule
Expand Down Expand Up @@ -5378,6 +5440,12 @@ static PyMethodDef rclpy_methods[] = {
"rclpy_get_node_names_and_namespaces", rclpy_get_node_names_and_namespaces, METH_VARARGS,
"Get node names and namespaces list from graph API."
},
{
"rclpy_get_node_names_and_namespaces_with_enclaves",
rclpy_get_node_names_and_namespaces_with_enclaves,
METH_VARARGS,
"Get node names, namespaces, and enclaves list from graph API."
},
{
"rclpy_get_node_parameters", rclpy_get_node_parameters, METH_VARARGS,
"Get the initial parameters for a node from the command line."
Expand Down
4 changes: 4 additions & 0 deletions rclpy/test/test_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ def test_node_names_and_namespaces(self):
# test that it doesn't raise
self.node.get_node_names_and_namespaces()

def test_node_names_and_namespaces_with_enclaves(self):
# test that it doesn't raise
self.node.get_node_names_and_namespaces_with_enclaves()

def assert_qos_equal(self, expected_qos_profile, actual_qos_profile, *, is_publisher):
# Depth and history are skipped because they are not retrieved.
self.assertEqual(
Expand Down