From 20d8a7f3ac8cdd594f1732bb4f04242e24786b9c Mon Sep 17 00:00:00 2001 From: CyberNative AI LLC Date: Wed, 21 Oct 2026 03:13:45 -0700 Subject: [PATCH] Support Python 3.15: bump PyO3 to 0.29 PyO3 0.26 stops the build on Python 3.15 ("newer than PyO3's maximum supported version (3.14)"). There is no cp315 wheel, so `pip install loro` on 3.15 builds the sdist and fails. PyO3 0.29 supports CPython 3.8 to 3.15. - pyo3 0.26.0 -> 0.29.3 (`cargo update -p pyo3`). - Migrate the APIs that 0.29 removed: `Bound::downcast` -> `cast`, `PyObject` -> `Py`, `Python::with_gil` -> `Python::attach`, and the two-lifetime `FromPyObject` for `LoroValue`. - Add `from_py_object` to the `Clone` pyclasses. PyO3 now asks for this opt-in to keep extracting them by value, so behaviour is unchanged and the build has no deprecation warnings. - Keep `gil_used = true` on the module. PyO3 0.28 changed the default, so without it the free-threaded build would load loro without the GIL. - CI: build 3.15 wheels. Fixes #29 --- .github/workflows/CI.yml | 8 +++--- Cargo.lock | 49 +++++++++-------------------------- Cargo.toml | 2 +- src/awareness.rs | 8 +++--- src/container/counter.rs | 2 +- src/container/list.rs | 2 +- src/container/map.rs | 2 +- src/container/movable_list.rs | 2 +- src/container/text.rs | 10 +++---- src/container/tree.rs | 4 +-- src/container/unknown.rs | 2 +- src/container/utils.rs | 2 +- src/convert.rs | 20 +++++++------- src/doc.rs | 22 ++++++++-------- src/event.rs | 24 ++++++++--------- src/lib.rs | 2 +- src/undo.rs | 6 ++--- src/value.rs | 22 +++++++++------- src/version.rs | 8 +++--- 19 files changed, 87 insertions(+), 110 deletions(-) diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 9be9b07..d513615 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -31,7 +31,7 @@ jobs: target: s390x - runner: ubuntu-22.04 target: ppc64le - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -63,7 +63,7 @@ jobs: target: aarch64 - runner: ubuntu-22.04 target: armv7 - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -91,7 +91,7 @@ jobs: target: x64 - runner: windows-latest target: x86 - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 @@ -119,7 +119,7 @@ jobs: target: x86_64 - runner: macos-15 target: aarch64 - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14", "3.15"] steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 diff --git a/Cargo.lock b/Cargo.lock index 24c4376..4e4d899 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -420,12 +420,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "indoc" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5" - [[package]] name = "itertools" version = "0.11.0" @@ -686,15 +680,6 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "nonmax" version = "0.5.5" @@ -917,35 +902,32 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.26.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba0117f4212101ee6544044dae45abe1083d30ce7b29c4b5cbdfa2354e07383" +checksum = "700d18fa267b73b9b521fd7e13580e2f446916f176cacee1ab63fcc8191f1655" dependencies = [ - "indoc", "libc", - "memoffset", "once_cell", "portable-atomic", "pyo3-build-config", "pyo3-ffi", "pyo3-macros", - "unindent", ] [[package]] name = "pyo3-build-config" -version = "0.26.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fc6ddaf24947d12a9aa31ac65431fb1b851b8f4365426e182901eabfb87df5f" +checksum = "7b3fc0c4d08f6bb10e71fe39dfb9e2f59c6eb6854e22ec8092f50c69a4499adb" dependencies = [ "target-lexicon", ] [[package]] name = "pyo3-ffi" -version = "0.26.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "025474d3928738efb38ac36d4744a74a400c901c7596199e20e45d98eb194105" +checksum = "dfc0b8e19df29aad7086cf977bb0c2a2f143e30567eb113e9cf72b62ca698330" dependencies = [ "libc", "pyo3-build-config", @@ -953,9 +935,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.26.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e64eb489f22fe1c95911b77c44cc41e7c19f3082fc81cce90f657cdc42ffded" +checksum = "6100e8a4b5eba53afaa5ed078364851a0b2499c553a44c31026b929049b49dc6" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -965,13 +947,12 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.26.0" +version = "0.29.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100246c0ecf400b475341b8455a9213344569af29a3c841d29270e53102e0fcf" +checksum = "6143877a16e82b5a727b7127ff4cd86858a24a28d745d72f43e6f227c7b1bdb3" dependencies = [ "heck 0.5.0", "proc-macro2", - "pyo3-build-config", "quote", "syn 2.0.93", ] @@ -1281,9 +1262,9 @@ dependencies = [ [[package]] name = "target-lexicon" -version = "0.13.2" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e502f78cdbb8ba4718f566c418c52bc729126ffd16baee5baa718cf25dd5a69a" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" [[package]] name = "thiserror" @@ -1404,12 +1385,6 @@ version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" -[[package]] -name = "unindent" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7de7d73e1754487cb58364ee906a499937a0dfabd86bcb980fa99ec8c8fa2ce" - [[package]] name = "valuable" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 3b3b8f6..6b9c757 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,5 +11,5 @@ crate-type = ["cdylib"] [dependencies] loro = { version = "1.16.2", features = ["counter", "jsonpath"] } rustc-hash = "2.1.1" -pyo3 = { version = "0.26.0" } +pyo3 = { version = "0.29" } serde_json = "1" diff --git a/src/awareness.rs b/src/awareness.rs index 4cd7336..b03c773 100644 --- a/src/awareness.rs +++ b/src/awareness.rs @@ -138,9 +138,9 @@ impl EphemeralStore { self.0.keys() } - pub fn subscribe_local_updates(&self, callback: PyObject) -> Subscription { + pub fn subscribe_local_updates(&self, callback: Py) -> Subscription { let subscription = self.0.subscribe_local_updates(Box::new(move |updates| { - Python::with_gil(|py| { + Python::attach(|py| { let b = callback.call1(py, (updates,)).unwrap(); b.extract::(py).unwrap() }) @@ -148,9 +148,9 @@ impl EphemeralStore { subscription.into() } - pub fn subscribe(&self, callback: PyObject) -> Subscription { + pub fn subscribe(&self, callback: Py) -> Subscription { let subscription = self.0.subscribe(Box::new(move |updates| { - Python::with_gil(|py| { + Python::attach(|py| { let b = callback .call1( py, diff --git a/src/container/counter.rs b/src/container/counter.rs index 3c0de87..116e7cf 100644 --- a/src/container/counter.rs +++ b/src/container/counter.rs @@ -21,7 +21,7 @@ impl LoroCounter { } } -#[pyclass(frozen)] +#[pyclass(frozen, from_py_object)] #[derive(Debug, Clone, Default)] pub struct LoroCounter(pub LoroCounterInner); diff --git a/src/container/list.rs b/src/container/list.rs index 649a558..6a47009 100644 --- a/src/container/list.rs +++ b/src/container/list.rs @@ -17,7 +17,7 @@ use crate::{ use super::{Container, Cursor, Side}; -#[pyclass(frozen, sequence)] +#[pyclass(frozen, sequence, from_py_object)] #[derive(Debug, Clone, Default)] pub struct LoroList(pub LoroListInner); diff --git a/src/container/map.rs b/src/container/map.rs index a4f0f7f..12116b4 100644 --- a/src/container/map.rs +++ b/src/container/map.rs @@ -17,7 +17,7 @@ pub fn register_class(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } -#[pyclass(frozen, mapping)] +#[pyclass(frozen, mapping, from_py_object)] #[derive(Debug, Clone, Default)] pub struct LoroMap(pub LoroMapInner); diff --git a/src/container/movable_list.rs b/src/container/movable_list.rs index 454e29c..159bf5a 100644 --- a/src/container/movable_list.rs +++ b/src/container/movable_list.rs @@ -16,7 +16,7 @@ use pyo3::{ use super::{Container, Cursor, Side}; -#[pyclass(frozen, sequence)] +#[pyclass(frozen, sequence, from_py_object)] #[derive(Debug, Clone, Default)] pub struct LoroMovableList(pub LoroMovableListInner); diff --git a/src/container/text.rs b/src/container/text.rs index 4e1dcf1..0a3fbf2 100644 --- a/src/container/text.rs +++ b/src/container/text.rs @@ -22,7 +22,7 @@ pub fn register_class(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } -#[pyclass(frozen)] +#[pyclass(frozen, from_py_object)] #[derive(Debug, Clone, Default)] pub struct LoroText(pub LoroTextInner); @@ -177,7 +177,7 @@ impl LoroText { let mut buf = [0u8; 4]; let as_str = ch.encode_utf8(&mut buf); Ok(PyString::new(py, as_str).into_any()) - } else if let Ok(slice) = key.downcast::() { + } else if let Ok(slice) = key.cast::() { let text = self.0.to_string(); let py_str = PyString::new(py, &text); py_str.get_item(slice) @@ -504,7 +504,7 @@ impl LoroText { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Side { Left = -1, @@ -512,7 +512,7 @@ pub enum Side { Right = 1, } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum PositionType { Bytes, @@ -534,7 +534,7 @@ impl From for PosType { } } -#[pyclass(str, frozen)] +#[pyclass(str, frozen, from_py_object)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Cursor(pub loro::cursor::Cursor); diff --git a/src/container/tree.rs b/src/container/tree.rs index 3ff71ba..b8328f1 100644 --- a/src/container/tree.rs +++ b/src/container/tree.rs @@ -19,7 +19,7 @@ pub fn register_class(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } -#[pyclass(frozen)] +#[pyclass(frozen, from_py_object)] #[derive(Debug, Clone, Default)] pub struct LoroTree(pub LoroTreeInner); @@ -373,7 +373,7 @@ impl LoroTree { } /// A tree node in the [LoroTree]. -#[pyclass(str, get_all, set_all)] +#[pyclass(str, get_all, set_all, from_py_object)] #[derive(Debug, Clone)] pub struct TreeNode { /// ID of the tree node. diff --git a/src/container/unknown.rs b/src/container/unknown.rs index a5c14f5..9d33adf 100644 --- a/src/container/unknown.rs +++ b/src/container/unknown.rs @@ -3,7 +3,7 @@ use pyo3::prelude::*; use crate::{doc::LoroDoc, value::ContainerID}; -#[pyclass(frozen)] +#[pyclass(frozen, from_py_object)] #[derive(Debug, Clone)] pub struct LoroUnknown(pub LoroUnknownInner); diff --git a/src/container/utils.rs b/src/container/utils.rs index cb9c37f..7cea23d 100644 --- a/src/container/utils.rs +++ b/src/container/utils.rs @@ -14,7 +14,7 @@ pub enum SliceOrInt<'py> { pub fn py_any_to_loro_values(obj: &Bound<'_, PyAny>) -> PyResult> { let sequence = obj - .downcast::() + .cast::() .map_err(|_| PyTypeError::new_err("can only assign an iterable to a slice"))?; let length = sequence.len()?; diff --git a/src/convert.rs b/src/convert.rs index e25fe61..ae46331 100644 --- a/src/convert.rs +++ b/src/convert.rs @@ -49,10 +49,10 @@ pub fn pyobject_to_container_id( obj: &Bound<'_, PyAny>, ty: ContainerType, ) -> PyResult { - if let Ok(value) = obj.downcast::() { + if let Ok(value) = obj.cast::() { return Ok(loro::ContainerID::new_root(value.to_str()?, ty.into())); } - if let Ok(value) = obj.downcast::() { + if let Ok(value) = obj.cast::() { return Ok(loro::ContainerID::from(value.get())); } @@ -73,27 +73,27 @@ pub fn pyobject_to_loro_value(obj: &Bound<'_, PyAny>) -> PyResult() { return Ok(loro::LoroValue::Double(value)); } - if let Ok(value) = obj.downcast::() { + if let Ok(value) = obj.cast::() { return Ok(loro::LoroValue::Binary(loro::LoroBinaryValue::from( value.as_bytes().to_vec(), ))); } - if let Ok(value) = obj.downcast::() { + if let Ok(value) = obj.cast::() { return Ok(loro::LoroValue::String(loro::LoroStringValue::from( value.to_string(), ))); } - if let Ok(value) = obj.downcast::() { + if let Ok(value) = obj.cast::() { let mut list = Vec::with_capacity(value.len()); for item in value.iter() { list.push(pyobject_to_loro_value(&item)?); } return Ok(loro::LoroValue::List(loro::LoroListValue::from(list))); } - if let Ok(value) = obj.downcast::() { + if let Ok(value) = obj.cast::() { let mut map = FxHashMap::default(); for (key, value) in value.iter() { - if key.downcast::().is_ok() { + if key.cast::().is_ok() { map.insert(key.to_string(), pyobject_to_loro_value(&value)?); } else { return Err(PyTypeError::new_err( @@ -103,10 +103,10 @@ pub fn pyobject_to_loro_value(obj: &Bound<'_, PyAny>) -> PyResult() { + if let Ok(value) = obj.cast::() { let mut map = FxHashMap::default(); for key in value.keys()? { - if key.downcast::().is_ok() { + if key.cast::().is_ok() { map.insert( key.to_string(), pyobject_to_loro_value(&value.get_item(key).unwrap())?, @@ -119,7 +119,7 @@ pub fn pyobject_to_loro_value(obj: &Bound<'_, PyAny>) -> PyResult() { + if let Ok(value) = obj.cast::() { return Ok(loro::LoroValue::Container(value.get().clone().into())); } Err(PyTypeError::new_err("Invalid LoroValue")) diff --git a/src/doc.rs b/src/doc.rs index 23cb0e6..d262d07 100644 --- a/src/doc.rs +++ b/src/doc.rs @@ -1157,7 +1157,7 @@ pub struct PreCommitCallbackPayload { pub modifier: ChangeModifier, } -#[pyclass] +#[pyclass(from_py_object)] #[derive(Debug, Clone)] pub struct ChangeModifier(loro::ChangeModifier); @@ -1240,7 +1240,7 @@ impl Display for ImportStatus { } } -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct PosQueryResult { pub update: Option, @@ -1253,7 +1253,7 @@ impl Display for PosQueryResult { } } -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct AbsolutePosition { pub pos: usize, @@ -1267,7 +1267,7 @@ impl Display for AbsolutePosition { } } -#[pyclass] +#[pyclass(from_py_object)] #[derive(Debug, Clone)] pub enum ExportMode { Snapshot {}, @@ -1280,7 +1280,7 @@ pub enum ExportMode { /// This struct supports reverse repr: [CounterSpan]'s from can be less than to. But we should use it conservatively. /// We need this because it'll make merging deletions easier. -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct IdSpan { pub peer: PeerID, @@ -1306,7 +1306,7 @@ impl IdSpan { /// /// But we should use it behavior conservatively. /// If it is not necessary to be reverse, it should not. -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct CounterSpan { pub start: Counter, @@ -1327,7 +1327,7 @@ impl Display for CounterSpan { } } -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone)] pub struct ChangeMeta { /// Lamport timestamp of the Change @@ -1351,7 +1351,7 @@ impl Display for ChangeMeta { } } -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone)] pub struct ImportBlobMetadata { /// The partial start version vector. @@ -1379,7 +1379,7 @@ impl Display for ImportBlobMetadata { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum EncodedBlobMode { Snapshot, @@ -1389,7 +1389,7 @@ pub enum EncodedBlobMode { Updates, } -#[pyclass(str)] +#[pyclass(str, from_py_object)] #[derive(Debug, Clone, Default)] pub struct StyleConfigMap(loro::StyleConfigMap); @@ -1435,7 +1435,7 @@ impl Display for StyleConfigMap { /// - After: when inserting new text after this style, the new text should inherit this style. /// - Both: when inserting new text before or after this style, the new text should inherit this style. /// - Null: when inserting new text before or after this style, the new text should **not** inherit this style. -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Clone, Copy, Eq, PartialEq, Debug, Hash)] pub enum ExpandType { Before, diff --git a/src/event.rs b/src/event.rs index 28e30ed..899dded 100644 --- a/src/event.rs +++ b/src/event.rs @@ -56,7 +56,7 @@ impl fmt::Display for DiffEvent { } /// The kind of the event trigger. -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum EventTriggerKind { /// The event is triggered by a local transaction. @@ -77,7 +77,7 @@ impl fmt::Display for EventTriggerKind { } } -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone)] pub struct PathItem { pub container: ContainerID, @@ -94,7 +94,7 @@ impl fmt::Display for PathItem { } } -#[pyclass(get_all, str)] +#[pyclass(get_all, str, from_py_object)] #[derive(Debug, Clone)] /// A diff of a container. pub struct ContainerDiff { @@ -125,7 +125,7 @@ impl fmt::Display for ContainerDiff { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub enum Index { Key { key: String }, @@ -143,7 +143,7 @@ impl fmt::Display for Index { } } -#[pyclass(get_all)] +#[pyclass(get_all, from_py_object)] #[derive(Debug, Clone)] pub enum Diff { /// A list diff. @@ -187,7 +187,7 @@ impl fmt::Display for Diff { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub enum TextDelta { Retain { @@ -241,7 +241,7 @@ impl fmt::Display for TextDelta { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub enum ListDiffItem { /// Insert a new element into the list. @@ -290,7 +290,7 @@ impl fmt::Display for ListDiffItem { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub struct MapDelta { /// All the updated keys and their new values. @@ -315,7 +315,7 @@ impl fmt::Display for MapDelta { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub struct TreeDiff { pub diff: Vec, @@ -335,7 +335,7 @@ impl fmt::Display for TreeDiff { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub struct TreeDiffItem { pub target: TreeID, @@ -352,7 +352,7 @@ impl fmt::Display for TreeDiffItem { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub enum TreeExternalDiff { Create { @@ -449,7 +449,7 @@ impl Subscription { } } -#[pyclass(str)] +#[pyclass(str, from_py_object)] #[derive(Debug, Clone, Default)] pub struct DiffBatch(loro::event::DiffBatch); diff --git a/src/lib.rs b/src/lib.rs index 0cd3b58..2a23a62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,7 +13,7 @@ mod value; mod version; /// Python bindings for Loro -#[pymodule(name = "loro")] +#[pymodule(name = "loro", gil_used = true)] fn loro_py(_py: Python<'_>, m: &Bound<'_, PyModule>) -> PyResult<()> { doc::register_class(m)?; container::register_class(m)?; diff --git a/src/undo.rs b/src/undo.rs index c3f04f6..0e0f264 100644 --- a/src/undo.rs +++ b/src/undo.rs @@ -165,21 +165,21 @@ impl UndoManager { } } -#[pyclass(eq, eq_int)] +#[pyclass(eq, eq_int, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum UndoOrRedo { Undo, Redo, } -#[pyclass(get_all)] +#[pyclass(get_all, from_py_object)] #[derive(Debug, Clone)] pub struct UndoItemMeta { pub value: LoroValue, pub cursors: Vec, } -#[pyclass(get_all)] +#[pyclass(get_all, from_py_object)] #[derive(Debug, Clone)] pub struct CursorWithPos { pub cursor: Cursor, diff --git a/src/value.rs b/src/value.rs index eedea64..f619027 100644 --- a/src/value.rs +++ b/src/value.rs @@ -18,7 +18,7 @@ pub fn register_class(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } -#[pyclass(eq, str, get_all, set_all)] +#[pyclass(eq, str, get_all, set_all, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct ID { pub peer: u64, @@ -39,7 +39,7 @@ impl ID { } } -#[pyclass(eq, str, get_all, set_all)] +#[pyclass(eq, str, get_all, set_all, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct IdLp { pub peer: u64, @@ -52,7 +52,7 @@ impl Display for IdLp { } } -#[pyclass(eq, hash, str)] +#[pyclass(eq, hash, str, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ContainerType { Text {}, @@ -70,7 +70,7 @@ impl Display for ContainerType { } } -#[pyclass(eq, str, hash)] +#[pyclass(eq, str, hash, from_py_object)] #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum ContainerID { Root { @@ -90,7 +90,7 @@ impl Display for ContainerID { } } -#[pyclass(eq, str, eq_int)] +#[pyclass(eq, str, eq_int, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Ordering { Less, @@ -114,7 +114,7 @@ impl From for Ordering { } } -#[pyclass(eq, str, get_all, set_all)] +#[pyclass(eq, str, get_all, set_all, from_py_object)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct TreeID { pub peer: PeerID, @@ -137,7 +137,7 @@ impl TreeID { pub type TreeParentId = Option; -#[pyclass] +#[pyclass(from_py_object)] #[derive(Debug, Clone)] pub enum ValueOrContainer { Value { value: LoroValue }, @@ -176,9 +176,11 @@ impl ValueOrContainer { #[derive(Debug, Clone)] pub struct LoroValue(pub(crate) loro::LoroValue); -impl<'py> FromPyObject<'py> for LoroValue { - fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { - let value = pyobject_to_loro_value(ob)?; +impl<'a, 'py> FromPyObject<'a, 'py> for LoroValue { + type Error = PyErr; + + fn extract(ob: Borrowed<'a, 'py, PyAny>) -> PyResult { + let value = pyobject_to_loro_value(&ob)?; Ok(Self(value)) } } diff --git a/src/version.rs b/src/version.rs index fab836d..bbadbca 100644 --- a/src/version.rs +++ b/src/version.rs @@ -19,7 +19,7 @@ pub fn register_class(m: &Bound<'_, PyModule>) -> PyResult<()> { Ok(()) } -#[pyclass(str)] +#[pyclass(str, from_py_object)] #[derive(Debug, Clone, Default)] pub struct Frontiers(loro::Frontiers); @@ -78,7 +78,7 @@ impl From<&Frontiers> for loro::Frontiers { } } -#[pyclass(str)] +#[pyclass(str, from_py_object)] #[derive(Clone, Debug, PartialEq, Eq, Default)] pub struct VersionRange(loro::VersionRange); @@ -178,7 +178,7 @@ impl From for VersionRange { } } -#[pyclass(str)] +#[pyclass(str, from_py_object)] #[derive(Debug, Clone, PartialEq, Eq)] pub struct VersionVector(loro::VersionVector); @@ -358,7 +358,7 @@ impl From for VersionVector { } } -#[pyclass(str, get_all)] +#[pyclass(str, get_all, from_py_object)] #[derive(Debug, Clone)] pub struct VersionVectorDiff { pub retreat: VersionRange,