From 1375b659d406ece37095d546ce99db7285e86e14 Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Fri, 6 Mar 2026 18:20:47 +0000 Subject: [PATCH 1/3] chore: add branch placeholder zbobr_fix-36-issue-690 --- .zbobr/zbobr_fix-36-issue-690 | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 .zbobr/zbobr_fix-36-issue-690 diff --git a/.zbobr/zbobr_fix-36-issue-690 b/.zbobr/zbobr_fix-36-issue-690 new file mode 100644 index 00000000..e69de29b From 4a2d772946bc9499ca4cd43eddb87b68b5cc952d Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Sat, 7 Mar 2026 12:14:17 +0000 Subject: [PATCH 2/3] feat: stabilize accept_replies API (issue #690) Expose the now-stable ReplyKeyExpr enum and accept_replies API from upstream zenoh PR #2443. This adds: - ReplyKeyExpr enum (Any, MatchingQuery) with DEFAULT classattr - Query.accepts_replies() method - Querier.accept_replies getter property - accept_replies parameter to Session.get() and Session.declare_querier() - ReplyKeyExpr registered in module exports - Updated type stubs and docstrings Co-Authored-By: Claude Opus 4.6 --- src/lib.rs | 2 +- src/query.rs | 20 ++++++++++++++++++++ src/session.rs | 10 +++++++--- zenoh/__init__.pyi | 41 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 67 insertions(+), 6 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 36b64f7a..ff9abe6a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -68,7 +68,7 @@ pub(crate) mod zenoh { qos::{CongestionControl, Priority, Reliability}, query::{ ConsolidationMode, Parameters, Querier, Query, QueryConsolidation, QueryTarget, - Queryable, Reply, ReplyError, Selector, + Queryable, Reply, ReplyError, ReplyKeyExpr, Selector, }, sample::{Locality, Sample, SampleKind, SourceInfo}, scouting::{scout, Hello, Scout}, diff --git a/src/query.rs b/src/query.rs index 05f1391f..1c7dfbb6 100644 --- a/src/query.rs +++ b/src/query.rs @@ -45,6 +45,17 @@ impl QueryTarget { const DEFAULT: Self = Self::BestMatching; } +enum_mapper!(zenoh::query::ReplyKeyExpr: u8 { + Any, + MatchingQuery, +}); + +#[pymethods] +impl ReplyKeyExpr { + #[classattr] + const DEFAULT: Self = Self::MatchingQuery; +} + enum_mapper!(zenoh::query::ConsolidationMode: u8 { Auto, None, @@ -130,6 +141,10 @@ impl Query { Ok(self.get_ref()?.attachment().cloned().map_into()) } + fn accepts_replies(&self) -> PyResult { + Ok(self.get_ref()?.accepts_replies().into_pyres()?.into()) + } + #[allow(clippy::too_many_arguments)] #[pyo3(signature = (key_expr, payload, *, encoding = None, congestion_control = None, priority = None, express = None, attachment = None, timestamp = None))] fn reply( @@ -382,6 +397,11 @@ impl Querier { Ok(self.get_ref()?.key_expr().clone().into()) } + #[getter] + fn accept_replies(&self) -> PyResult { + Ok(self.get_ref()?.accept_replies().into()) + } + #[getter] fn matching_status(&self, py: Python) -> PyResult { Ok(wait(py, self.get_ref()?.matching_status())?.into()) diff --git a/src/session.rs b/src/session.rs index 7c3afac6..a4564afe 100644 --- a/src/session.rs +++ b/src/session.rs @@ -29,7 +29,7 @@ use crate::{ macros::{build, wrapper}, pubsub::{Publisher, Subscriber}, qos::{CongestionControl, Priority, Reliability}, - query::{Querier, QueryConsolidation, QueryTarget, Queryable, Reply, Selector}, + query::{Querier, QueryConsolidation, QueryTarget, Queryable, Reply, ReplyKeyExpr, Selector}, sample::{Locality, SourceInfo}, time::Timestamp, utils::{duration, wait, IntoPython, MapInto}, @@ -150,7 +150,7 @@ impl Session { } #[allow(clippy::too_many_arguments)] - #[pyo3(signature = (selector, handler = None, *, target = None, consolidation = None, timeout = None, congestion_control = None, priority = None, express = None, payload = None, encoding = None, attachment = None, allowed_destination = None, source_info = None, cancellation_token = None))] + #[pyo3(signature = (selector, handler = None, *, target = None, consolidation = None, accept_replies = None, timeout = None, congestion_control = None, priority = None, express = None, payload = None, encoding = None, attachment = None, allowed_destination = None, source_info = None, cancellation_token = None))] fn get( &self, py: Python, @@ -160,6 +160,7 @@ impl Session { #[pyo3(from_py_with = QueryConsolidation::from_py_opt)] consolidation: Option< QueryConsolidation, >, + accept_replies: Option, #[pyo3(from_py_with = duration)] timeout: Option, congestion_control: Option, priority: Option, @@ -176,6 +177,7 @@ impl Session { self.0.get(selector), target, consolidation, + accept_replies, timeout, congestion_control, priority, @@ -257,7 +259,7 @@ impl Session { } #[allow(clippy::too_many_arguments)] - #[pyo3(signature = (key_expr, *, target = None, consolidation = None, timeout = None, congestion_control = None, priority = None, express = None, allowed_destination = None))] + #[pyo3(signature = (key_expr, *, target = None, consolidation = None, accept_replies = None, timeout = None, congestion_control = None, priority = None, express = None, allowed_destination = None))] fn declare_querier( &self, py: Python, @@ -266,6 +268,7 @@ impl Session { #[pyo3(from_py_with = QueryConsolidation::from_py_opt)] consolidation: Option< QueryConsolidation, >, + accept_replies: Option, #[pyo3(from_py_with = duration)] timeout: Option, congestion_control: Option, priority: Option, @@ -276,6 +279,7 @@ impl Session { self.0.declare_querier(key_expr), target, consolidation, + accept_replies, timeout, congestion_control, priority, diff --git a/zenoh/__init__.pyi b/zenoh/__init__.pyi index 29a73e67..a9a0c8a0 100644 --- a/zenoh/__init__.pyi +++ b/zenoh/__init__.pyi @@ -805,7 +805,9 @@ class Query: By default, queries only accept replies whose key expression intersects with the query's. I.e. it's not possible to send reply with key expression ``foo/bar`` to a query with key expression ``baz/*``. - The query may contain special unstable parameter ``_anyke`` which enables disjoint replies. + To allow disjoint replies, use the ``accept_replies`` parameter with :attr:`ReplyKeyExpr.ANY` + in :meth:`Session.get` or :meth:`Session.declare_querier`. + Alternatively, the query may contain special parameter ``_anyke`` which also enables disjoint replies. See the :class:`Selector` documentation for more information about this parameter. See :ref:`query-reply` for more information on the query/reply paradigm. @@ -837,6 +839,10 @@ class Query: def attachment(self) -> ZBytes | None: """The attachment of this query, if any.""" + def accepts_replies(self) -> ReplyKeyExpr: + """Returns the :class:`ReplyKeyExpr` setting of this query, indicating whether replies + must match the query's key expression or can use any key expression.""" + def reply( self, key_expr: _IntoKeyExpr, @@ -965,6 +971,10 @@ class Querier: def key_expr(self) -> KeyExpr: """Returns the :class:`KeyExpr` this querier sends queries on.""" + @property + def accept_replies(self) -> ReplyKeyExpr: + """Returns the :class:`ReplyKeyExpr` setting of this querier.""" + @property def matching_status(self) -> bool: """Returns true if there are :class:`Queryable`\\s matching the Querier's key expression and target, false otherwise.""" @@ -1092,6 +1102,29 @@ QueryTarget.ALL.__doc__ = ( ) QueryTarget.ALL_COMPLETE.__doc__ = """Deliver the query to all queryables matching the query's key expression that are declared as complete.""" +@final +class ReplyKeyExpr(Enum): + """Controls whether replies to a query must match the query's key expression. + + :attr:`ReplyKeyExpr.MATCHING_QUERY` (default) means that replies must have a key expression + matching the query's key expression. + :attr:`ReplyKeyExpr.ANY` allows replies with any key expression, even if it doesn't match the query. + + It is set by the ``accept_replies`` parameter of :meth:`Session.get` or :meth:`Session.declare_querier` methods. + """ + + ANY = auto() + MATCHING_QUERY = auto() + + DEFAULT = MATCHING_QUERY + +ReplyKeyExpr.ANY.__doc__ = ( + """Accept replies whose key expressions may not match the query key expression.""" +) +ReplyKeyExpr.MATCHING_QUERY.__doc__ = ( + """Accept replies whose key expressions match the query key expression.""" +) + @final @_unstable class Reliability(Enum): @@ -1315,7 +1348,7 @@ class Selector: for the exhaustive list): - ``[unstable]`` ``_time``: used to express interest in only values dated within a certain time range, values for this parameter must be readable by the Zenoh Time DSL for the value to be considered valid. - - ``[unstable]`` ``_anyke``: used in queries to express interest in replies coming from any key expression. By default, only replies whose key expression match query's key expression are accepted. ``_anyke`` disables the query-reply key expression matching check. + - ``_anyke``: used in queries to express interest in replies coming from any key expression. By default, only replies whose key expression match query's key expression are accepted. ``_anyke`` disables the query-reply key expression matching check. See also :attr:`ReplyKeyExpr.ANY` as the preferred API for this functionality. See also: :ref:`key-expressions`, :ref:`query-parameters` """ @@ -1438,6 +1471,7 @@ class Session: *, target: QueryTarget | None = None, consolidation: _IntoQueryConsolidation | None = None, + accept_replies: ReplyKeyExpr | None = None, timeout: float | int | None = None, congestion_control: CongestionControl | None = None, priority: Priority | None = None, @@ -1462,6 +1496,7 @@ class Session: *, target: QueryTarget | None = None, consolidation: _IntoQueryConsolidation | None = None, + accept_replies: ReplyKeyExpr | None = None, timeout: float | int | None = None, congestion_control: CongestionControl | None = None, priority: Priority | None = None, @@ -1486,6 +1521,7 @@ class Session: *, target: QueryTarget | None = None, consolidation: _IntoQueryConsolidation | None = None, + accept_replies: ReplyKeyExpr | None = None, timeout: float | int | None = None, congestion_control: CongestionControl | None = None, priority: Priority | None = None, @@ -1584,6 +1620,7 @@ class Session: *, target: QueryTarget | None = None, consolidation: _IntoQueryConsolidation | None = None, + accept_replies: ReplyKeyExpr | None = None, timeout: float | int | None = None, congestion_control: CongestionControl | None = None, priority: Priority | None = None, From 8ec57737ac92a40821ebfdc23971a60009b7dedc Mon Sep 17 00:00:00 2001 From: Michael Ilyin Date: Mon, 9 Mar 2026 14:57:18 +0100 Subject: [PATCH 3/3] tmp zbobr removed --- .zbobr/zbobr_fix-36-issue-690 | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .zbobr/zbobr_fix-36-issue-690 diff --git a/.zbobr/zbobr_fix-36-issue-690 b/.zbobr/zbobr_fix-36-issue-690 deleted file mode 100644 index e69de29b..00000000