Skip to content

feat(python): expand Python bindings with actions, SHM, graph, QoS, and generic bridge#110

Open
YuanYuYuan wants to merge 12 commits intomainfrom
dev/python-2
Open

feat(python): expand Python bindings with actions, SHM, graph, QoS, and generic bridge#110
YuanYuYuan wants to merge 12 commits intomainfrom
dev/python-2

Conversation

@YuanYuYuan
Copy link
Collaborator

Summary

Expands the ros_z_py Python bindings from a minimal 5-type stub to a
near-complete API covering pub/sub, services, actions, graph discovery,
QoS, SHM, and context lifecycle.

Key Changes

Generic message bridge — replaced per-type factory match statements with
RawBytesMessage/RawBytesCdrSerdes (identity serializer). Any message type
registered in ros_z_msgs_py now works with pub/sub and services without
per-type Rust code.

Actions — full Python↔Python action client/server via RawBytesAction
(DynActionMessage for Goal/Result/Feedback). ZActionClient and
ZActionServer are driven from a global tokio runtime; feedback is bridged
from tokio mpsc to flume for blocking-friendly Python access.

Graph discoveryget_topic_names_and_types, get_node_names,
get_service_names_and_types, count_publishers, count_subscribers on
ZNode.

QoS classPyQosProfile with named constructor, static presets
(default, sensor_data, parameters, services), and backward-compatible
dict support.

Context builder — added with_mode, with_router_endpoint, with_remap_rule(s),
with_enclave, with_shm_enabled, with_shm_pool_size, with_shm_threshold,
and shutdown() / context-manager protocol.

Callback subscriptionscreate_subscriber(callback=fn) uses
build_with_callback; queue-based subscriptions unchanged.

Node propertiesname, namespace, fully_qualified_name.

Fixcheck-example-coverage.nu path corrected after the crates/
reorganisation in #92.

…nd generic bridge

Closes the gap between the Python API and Rust API:

- Generic message bridge (RawBytesMessage/RawBytesCdrSerdes): any
  registered message type works for pub/sub and services without
  per-type factory match statements
- Action client/server (PyZActionClient, PyZActionServer): full
  Python↔Python action support via RawBytesAction/DynActionMessage;
  uses a global tokio runtime for async ops
- Graph discovery: get_topic_names_and_types, get_node_names,
  get_service_names_and_types, count_publishers, count_subscribers
- QoS class (PyQosProfile): type-safe Python QoS with presets and
  backward-compatible dict support
- Context builder: full builder with mode, router, remap, enclave,
  SHM (with_shm_enabled, with_shm_pool_size, with_shm_threshold),
  and clean shutdown
- Callback subscriptions: create_subscriber(callback=fn) pattern
- Node properties: name, namespace, fully_qualified_name
- Fix check-example-coverage.nu path after crates/ reorganization
Add with_goal_type_info / with_result_type_info / with_feedback_type_info
to ZActionClientBuilder and ZActionServerBuilder so callers can supply
type hashes determined at runtime (e.g. from Python message classes).

Also relax extract_type_info_from_class to treat __hash__ as optional:
inline msgspec structs without a RIHS01 hash now use zero TypeHash and
work for Python-to-Python communication without raising an error.
- examples/action_demo.py: CountTo action demo with cancellation support
- tests/test_action.py: 7 test cases covering creation, send/result,
  feedback, status, cancellation, rejection, abort, and timeout
- book/src/chapters/python.md: Actions section with message types,
  server/client lifecycle, GoalStatus table, and interop note
@github-actions
Copy link

github-actions bot commented Mar 4, 2026

PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://ZettaScaleLabs.github.io/ros-z/pr-preview/pr-110/

Built to branch gh-pages at 2026-03-05 12:25 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

…gs registry

Inline msgspec types (like test action types) are not registered in the
ros_z_msgs type registry, causing 'Unknown message type' errors in CI.

Switch action goal/result/feedback serialization to msgspec.msgpack
directly: store Python class objects in the action wrappers and use
msgspec.msgpack.encode/decode for Python-to-Python actions, bypassing
the CDR registry lookup entirely.
…ructs

The server only needs goal_type (to decode incoming goal bytes).
Result and feedback are only encoded server-side, not decoded, so no
class reference is needed. Remove the dead fields to fix the CI
-D warnings build error.
1. `is_cancel_requested` missing `#[getter]`: accessing it as a Python
   property returned the method object (always truthy), causing the
   server to cancel every goal after the first feedback.

2. `status_arc` initialized to Unknown: immediately after `send_goal`
   the status bridge task hadn't fired yet, so `handle.status` returned
   0 (Unknown) instead of 1 (Accepted). Initialize to Accepted and
   read the current watch value on subscription.

3. Cancel requests not handled in polling mode: the server had a
   background result-request handler but no cancel-request handler.
   `client.cancel()` sent a cancel service request and waited forever
   for a reply. Add `handle_cancel_requests_legacy_inner` and spawn it
   alongside the result handler.

4. `send_goal` hangs when no server is present: the flume channel's
   persistent sender kept `rx.recv_async()` alive after the Zenoh
   query expired. Add an 11 s tokio timeout so callers get a clear
   RuntimeError instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant