From c81eead9e8545bfcc88f3007f73663cfce0f6258 Mon Sep 17 00:00:00 2001 From: Erik Johnston Date: Sat, 9 May 2026 13:48:02 +0100 Subject: [PATCH] Add helpers and visibility for the upcoming Event port Small prerequisites for porting the Python EventBase hierarchy to Rust: - duration: make `from_milliseconds` const and add an `IntoPyObject` impl for owned `SynapseDuration`, so the new Rust `Event.sticky_duration()` can return one directly to Python. - internal_metadata: rename `copy()` to `deep_copy()` (matching the new naming used by the rest of the events module) and make `new()` callable from sibling modules. - json_object: expose `object` as a `pub` field and add a `get_field` helper so the new Event class can read from it without going through Python. - signatures, unsigned: add `deep_copy()` methods so the new Event class can implement its own deep-copy. --- rust/src/duration.rs | 15 ++++++++++++++- rust/src/events/internal_metadata.rs | 4 ++-- rust/src/events/json_object.rs | 8 +++++++- rust/src/events/signatures.rs | 10 ++++++++++ rust/src/events/unsigned.rs | 10 ++++++++-- 5 files changed, 41 insertions(+), 6 deletions(-) diff --git a/rust/src/duration.rs b/rust/src/duration.rs index a3dbe919b2..cfa42ed132 100644 --- a/rust/src/duration.rs +++ b/rust/src/duration.rs @@ -35,13 +35,26 @@ pub struct SynapseDuration { impl SynapseDuration { /// For now we only need to create durations from milliseconds. - pub fn from_milliseconds(milliseconds: u64) -> Self { + pub const fn from_milliseconds(milliseconds: u64) -> Self { Self { microseconds: milliseconds * 1_000, } } } +impl<'py> IntoPyObject<'py> for SynapseDuration { + type Target = PyAny; + type Output = Bound<'py, Self::Target>; + type Error = PyErr; + + fn into_pyobject(self, py: Python<'py>) -> Result { + let duration_module = duration_module(py)?; + let kwargs = [("microseconds", self.microseconds)].into_py_dict(py)?; + let duration_instance = duration_module.call_method("Duration", (), Some(&kwargs))?; + Ok(duration_instance.into_bound()) + } +} + impl<'py> IntoPyObject<'py> for &SynapseDuration { type Target = PyAny; type Output = Bound<'py, Self::Target>; diff --git a/rust/src/events/internal_metadata.rs b/rust/src/events/internal_metadata.rs index 6fd3d06b00..0d3536d236 100644 --- a/rust/src/events/internal_metadata.rs +++ b/rust/src/events/internal_metadata.rs @@ -510,7 +510,7 @@ fn attr_err(val: Option, name: &str) -> PyResult { #[pymethods] impl EventInternalMetadata { #[new] - fn new(dict: &Bound<'_, PyDict>) -> PyResult { + pub fn new(dict: &Bound<'_, PyDict>) -> PyResult { let mut data = Vec::with_capacity(dict.len()); for (key, value) in dict.iter() { @@ -536,7 +536,7 @@ impl EventInternalMetadata { }) } - fn copy(&self) -> PyResult { + pub fn deep_copy(&self) -> PyResult { let guard = self.read_inner()?; Ok(EventInternalMetadata { inner: Arc::new(RwLock::new(guard.clone())), diff --git a/rust/src/events/json_object.rs b/rust/src/events/json_object.rs index 2c4be1c87b..3e118b8ebc 100644 --- a/rust/src/events/json_object.rs +++ b/rust/src/events/json_object.rs @@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize}; #[pyclass(mapping, frozen)] #[serde(transparent)] pub struct JsonObject { - object: Arc, serde_json::Value>>, + pub object: Arc, serde_json::Value>>, } #[pymethods] @@ -193,6 +193,12 @@ impl JsonObject { } } +impl JsonObject { + pub fn get_field(&self, key: &str) -> Option<&serde_json::Value> { + self.object.get(key) + } +} + /// Helper class returned by `JsonObject.keys()` to act as a view into the keys /// of the object. /// diff --git a/rust/src/events/signatures.rs b/rust/src/events/signatures.rs index 0f2acd5c9b..933ace881d 100644 --- a/rust/src/events/signatures.rs +++ b/rust/src/events/signatures.rs @@ -36,6 +36,16 @@ pub struct Signatures { inner: Arc>>>, } +impl Signatures { + pub fn deep_copy(&self) -> Self { + let signatures = self.inner.read().expect("lock poisoned").clone(); // Deep copy the inner map + + Self { + inner: Arc::new(RwLock::new(signatures)), + } + } +} + #[pymethods] impl Signatures { #[new] diff --git a/rust/src/events/unsigned.rs b/rust/src/events/unsigned.rs index c41ed7e6e1..95ad8e0490 100644 --- a/rust/src/events/unsigned.rs +++ b/rust/src/events/unsigned.rs @@ -100,6 +100,12 @@ impl Unsigned { .write() .map_err(|_| PyRuntimeError::new_err("Unsigned lock poisoned")) } + + pub fn deep_copy(&self) -> Self { + Self { + inner: Arc::new(RwLock::new(self.py_read().expect("lock poisoned").clone())), + } + } } #[pymethods] @@ -264,11 +270,11 @@ impl Unsigned { } } - fn for_persistence<'py>(&self, py: Python<'py>) -> PyResult> { + pub fn for_persistence<'py>(&self, py: Python<'py>) -> PyResult> { Ok(pythonize(py, &self.py_read()?.persisted_fields)?) } - fn for_event<'py>(&self, py: Python<'py>) -> PyResult> { + pub fn for_event<'py>(&self, py: Python<'py>) -> PyResult> { Ok(pythonize(py, &*self.py_read()?)?) } }