From e36c8e163729fe5622a6ef9baf11b7bdd84553f8 Mon Sep 17 00:00:00 2001 From: Maarten van Gompel Date: Mon, 18 Nov 2024 16:58:10 +0100 Subject: [PATCH] upgrading to pyo3 0.23 --- Cargo.toml | 6 ++-- src/annotation.rs | 18 +++++++---- src/annotationdata.rs | 69 +++++++++++++++++++++++++++------------- src/annotationdataset.rs | 5 ++- src/annotationstore.rs | 2 +- src/lib.rs | 2 +- src/query.rs | 40 ++++++++++------------- src/resources.rs | 47 +++++++++++++-------------- src/selector.rs | 18 +++++------ src/substore.rs | 5 ++- src/textselection.rs | 52 +++++++++++++++++++----------- 11 files changed, 153 insertions(+), 111 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eff42c9..276fb91 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,10 +17,10 @@ name = "stam" crate-type = ["cdylib"] [dependencies] -pyo3 = { version = "0.22.3", features = ["chrono"] } +pyo3 = { version = "0.23.1", features = ["chrono"] } rayon = "1.10.0" -stam = "0.16.4" -stam-tools = "0.9.1" +stam = "0.16.5" +stam-tools = "0.9.2" [features] default = ["pyo3/extension-module"] diff --git a/src/annotation.rs b/src/annotation.rs index 6b71fa1..8fe7f6d 100644 --- a/src/annotation.rs +++ b/src/annotation.rs @@ -3,6 +3,7 @@ use pyo3::exceptions::{PyIndexError, PyRuntimeError}; use pyo3::prelude::*; use pyo3::pyclass::CompareOp; use pyo3::types::*; +use pyo3::IntoPyObject; use std::borrow::Cow; use std::ops::FnOnce; use std::sync::{Arc, RwLock}; @@ -47,7 +48,10 @@ impl PyAnnotation { store: Arc>, py: Python<'py>, ) -> Bound<'py, PyAny> { - Self::new(handle, store).into_py(py).into_bound(py) + Self::new(handle, store) + .into_pyobject(py) + .expect("infallible") + .into_any() } } @@ -124,7 +128,7 @@ impl PyAnnotation { /// /// If you are sure an annotation only references a single contingent text slice or are okay with slices being concatenated, then you can use `str()` instead. fn text<'py>(&self, py: Python<'py>) -> Bound<'py, PyList> { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|annotation| { for text in annotation.text() { list.append(text).ok(); @@ -302,7 +306,7 @@ impl PyAnnotation { /// Returns a list of resources this annotation refers to #[pyo3(signature = (limit=None))] fn resources<'py>(&self, limit: Option, py: Python<'py>) -> Bound<'py, PyList> { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|annotation| { for (i, resource) in annotation.resources().enumerate() { list.append(PyTextResource::new_py( @@ -324,7 +328,7 @@ impl PyAnnotation { /// Returns the resources this annotation refers to (as metadata) in a list #[pyo3(signature = (limit=None))] fn datasets<'py>(&self, limit: Option, py: Python<'py>) -> Bound<'py, PyList> { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|annotation| { for (i, dataset) in annotation.datasets().enumerate() { list.append(PyAnnotationDataSet::new_py( @@ -579,7 +583,7 @@ impl PyAnnotation { py: Python<'py>, kwargs: Option>, ) -> PyResult> { - let alignments = PyList::empty_bound(py); + let alignments = PyList::empty(py); let storeclone = self.store.clone(); let mut annotations = false; if let Some(kwargs) = kwargs { @@ -594,7 +598,7 @@ impl PyAnnotation { if let (Some(left), Some(right)) = (annoiter.next(), annoiter.next()) { //complex transposition for (text1, text2) in left.textselections().zip(right.textselections()) { - let alignment = PyList::empty_bound(py); + let alignment = PyList::empty(py); if annotations { alignment .append(PyAnnotation::new_py(left.handle(), storeclone.clone(), py)) @@ -618,7 +622,7 @@ impl PyAnnotation { //simple transposition let mut textiter = annotation.textselections(); if let (Some(text1), Some(text2)) = (textiter.next(), textiter.next()) { - let alignment = PyList::empty_bound(py); + let alignment = PyList::empty(py); alignment .append(PyTextSelection::from_result_to_py(text1, &storeclone, py)) .map_err(|_| StamError::OtherError("failed to extract alignment"))?; diff --git a/src/annotationdata.rs b/src/annotationdata.rs index 61da6e1..08d915c 100644 --- a/src/annotationdata.rs +++ b/src/annotationdata.rs @@ -33,6 +33,18 @@ impl PyDataKey { ) -> PyDataKey { PyDataKey { set, handle, store } } + + pub(crate) fn new_py<'py>( + handle: DataKeyHandle, + set: AnnotationDataSetHandle, + store: Arc>, + py: Python<'py>, + ) -> Bound<'py, PyAny> { + Self::new(handle, set, store) + .into_pyobject(py) + .expect("infallible") + .into_any() + } } #[pymethods] @@ -54,12 +66,11 @@ impl PyDataKey { self.map(|datakey| Ok(datakey.id() == Some(other))) } - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (self.set == other.set && self.handle == other.handle).into_py(py), - CompareOp::Ne => (self.set != other.set || self.handle != other.handle).into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => self.set == other.set && self.handle == other.handle, + CompareOp::Ne => self.set != other.set || self.handle != other.handle, + _ => false, } } @@ -252,6 +263,18 @@ impl PyAnnotationData { ) -> PyAnnotationData { PyAnnotationData { set, handle, store } } + + pub(crate) fn new_py<'py>( + handle: AnnotationDataHandle, + set: AnnotationDataSetHandle, + store: Arc>, + py: Python<'py>, + ) -> Bound<'py, PyAny> { + Self::new(handle, set, store) + .into_pyobject(py) + .expect("infallible") + .into_any() + } } pub(crate) fn datavalue_from_py<'py>(value: Bound<'py, PyAny>) -> Result { @@ -288,18 +311,22 @@ pub(crate) fn datavalue_into_py<'py>( py: Python<'py>, ) -> Result, StamError> { match datavalue { - DataValue::String(s) => Ok(s.into_py(py).into_bound(py)), - DataValue::Float(f) => Ok(f.into_py(py).into_bound(py)), - DataValue::Int(v) => Ok(v.into_py(py).into_bound(py)), - DataValue::Bool(v) => Ok(v.into_py(py).into_bound(py)), - DataValue::Datetime(v) => Ok(v.into_py(py).into_bound(py)), + DataValue::String(s) => Ok(s.into_pyobject(py).expect("infallible").into_any()), + DataValue::Float(f) => Ok(f.into_pyobject(py).expect("infallible").into_any()), + DataValue::Int(v) => Ok(v.into_pyobject(py).expect("infallible").into_any()), + DataValue::Bool(v) => Ok( as Clone>::clone( + //ugly, compiler suggested this! doesn't work without for pyo3 0.23 + &v.into_pyobject(py).expect("infallible"), + ) + .into_any()), + DataValue::Datetime(v) => Ok(v.into_pyobject(py).expect("infallible").into_any()), DataValue::Null => { //feels a bit hacky, but I can't find a PyNone to return as PyAny let x: Option = None; - Ok(x.into_py(py).into_bound(py)) + Ok(x.into_pyobject(py).expect("infallible").into_any()) } DataValue::List(v) => { - let pylist = PyList::empty_bound(py); + let pylist = PyList::empty(py); for item in v.iter() { let pyvalue = datavalue_into_py(item, py)?; pylist.append(pyvalue).expect("adding value to list"); @@ -337,12 +364,11 @@ impl PyDataValue { }) } - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (self.value == other.value).into_py(py), - CompareOp::Ne => (self.value != other.value).into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => self.value == other.value, + CompareOp::Ne => self.value != other.value, + _ => false, } } @@ -446,12 +472,11 @@ impl PyAnnotationData { self.map(|annotationdata| Ok(annotationdata.id() == Some(other))) } - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (self.set == other.set && self.handle == other.handle).into_py(py), - CompareOp::Ne => (self.set != other.set || self.handle != other.handle).into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => self.set == other.set && self.handle == other.handle, + CompareOp::Ne => self.set != other.set || self.handle != other.handle, + _ => false, } } diff --git a/src/annotationdataset.rs b/src/annotationdataset.rs index a95699d..31729c5 100644 --- a/src/annotationdataset.rs +++ b/src/annotationdataset.rs @@ -36,7 +36,10 @@ impl PyAnnotationDataSet { store: Arc>, py: Python<'py>, ) -> Bound<'py, PyAny> { - Self::new(handle, store).into_py(py).into_bound(py) + Self::new(handle, store) + .into_pyobject(py) + .expect("infallible") + .into_any() } } diff --git a/src/annotationstore.rs b/src/annotationstore.rs index 25b12b4..55d6943 100644 --- a/src/annotationstore.rs +++ b/src/annotationstore.rs @@ -43,7 +43,7 @@ impl PyAnnotationStore { #[pyo3(text_signature = "(self, id=None, file=None, string=None, config=None)")] fn new<'py>(kwargs: Option>, py: Python<'py>) -> PyResult { if let Some(kwargs) = kwargs { - let mut config = PyDict::new_bound(py); + let mut config = PyDict::new(py); for (key, value) in kwargs.iter() { match key.downcast()?.extract()? { "config" => { diff --git a/src/lib.rs b/src/lib.rs index a35cf2f..7cdb8c3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,7 +27,7 @@ const VERSION: &'static str = env!("CARGO_PKG_VERSION"); #[pymodule] fn stam(py: Python<'_>, m: Bound<'_, PyModule>) -> PyResult<()> { - m.add("StamError", py.get_type_bound::())?; + m.add("StamError", py.get_type::())?; m.add("VERSION", VERSION)?; m.add_class::()?; m.add_class::()?; diff --git a/src/query.rs b/src/query.rs index fc66e50..ce3db3c 100644 --- a/src/query.rs +++ b/src/query.rs @@ -500,9 +500,9 @@ pub(crate) fn query_to_python<'py>( store: Arc>, py: Python<'py>, ) -> Result, StamError> { - let results = PyList::empty_bound(py); + let results = PyList::empty(py); for resultitems in iter { - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); for (result, name) in resultitems.iter().zip(resultitems.names()) { if name.is_none() { continue; @@ -512,70 +512,62 @@ pub(crate) fn query_to_python<'py>( QueryResultItem::Annotation(annotation) => { dict.set_item( name, - PyAnnotation::new(annotation.handle(), store.clone()) - .into_py(py) - .into_bound(py), + PyAnnotation::new_py(annotation.handle(), store.clone(), py), ) .unwrap(); } QueryResultItem::AnnotationData(data) => { dict.set_item( name, - PyAnnotationData::new(data.handle(), data.set().handle(), store.clone()) - .into_py(py) - .into_bound(py), + PyAnnotationData::new_py( + data.handle(), + data.set().handle(), + store.clone(), + py, + ), ) .unwrap(); } QueryResultItem::DataKey(key) => { dict.set_item( name, - PyDataKey::new(key.handle(), key.set().handle(), store.clone()) - .into_py(py) - .into_bound(py), + PyDataKey::new_py(key.handle(), key.set().handle(), store.clone(), py), ) .unwrap(); } QueryResultItem::TextResource(resource) => { dict.set_item( name, - PyTextResource::new(resource.handle(), store.clone()) - .into_py(py) - .into_bound(py), + PyTextResource::new_py(resource.handle(), store.clone(), py), ) .unwrap(); } QueryResultItem::AnnotationDataSet(dataset) => { dict.set_item( name, - PyAnnotationDataSet::new(dataset.handle(), store.clone()) - .into_py(py) - .into_bound(py), + PyAnnotationDataSet::new_py(dataset.handle(), store.clone(), py), ) .unwrap(); } QueryResultItem::TextSelection(textselection) => { dict.set_item( name, - PyTextSelection::new( + PyTextSelection::new_py( textselection .as_ref() .expect("textselection must be bound") .clone(), textselection.resource().handle(), store.clone(), - ) - .into_py(py) - .into_bound(py), + py, + ), ) .unwrap(); } QueryResultItem::AnnotationSubStore(substore) => { dict.set_item( name, - PyAnnotationSubStore::new(substore.handle(), store.clone()) - .into_py(py) - .into_bound(py), + PyAnnotationSubStore::new_py(substore.handle(), store.clone(), py), ) .unwrap(); } diff --git a/src/resources.rs b/src/resources.rs index bd01071..715a3cd 100644 --- a/src/resources.rs +++ b/src/resources.rs @@ -37,7 +37,10 @@ impl PyTextResource { store: Arc>, py: Python<'py>, ) -> Bound<'py, PyAny> { - Self::new(handle, store).into_py(py).into_bound(py) + Self::new(handle, store) + .into_pyobject(py) + .expect("infallible") + .into_any() } } @@ -98,7 +101,7 @@ impl PyTextResource { let slice = slice .indices(resource.textlen().try_into().unwrap()) .expect("expected valid slice"); - Ok(PyString::new_bound( + Ok(PyString::new( py, resource .text_by_offset(&Offset::simple(slice.start as usize, slice.stop as usize))?, @@ -108,7 +111,7 @@ impl PyTextResource { /// 'Returns the full text of the resource (by value, aka a copy) fn text<'py>(&self, py: Python<'py>) -> PyResult> { - self.map(|resource| Ok(PyString::new_bound(py, resource.text()))) + self.map(|resource| Ok(PyString::new(py, resource.text()))) } /// Returns the length of the resources's text in unicode points (same as `len(self.text())` but more performant) @@ -137,7 +140,7 @@ impl PyTextResource { case_sensitive: Option, py: Python<'py>, ) -> Bound<'py, PyList> { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|res| { if case_sensitive == Some(false) { for (i, textselection) in res.find_text_nocase(fragment).enumerate() { @@ -183,7 +186,7 @@ impl PyTextResource { py: Python<'py>, ) -> Bound<'py, PyList> { let fragments: Vec<&str> = fragments.iter().map(|s| s.as_str()).collect(); - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|res| { let results = res.find_text_sequence( &fragments, @@ -249,13 +252,13 @@ impl PyTextResource { )) })?); } - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|res| { for (i, regexmatch) in res .find_text_regex(®exps, None, allow_overlap.unwrap_or(false))? .enumerate() { - let textselections = PyList::empty_bound(py); + let textselections = PyList::empty(py); for textselection in regexmatch.textselections() { textselections .append(PyTextSelection::from_result_to_py( @@ -265,7 +268,7 @@ impl PyTextResource { )) .ok(); } - let dict = PyDict::new_bound(py); + let dict = PyDict::new(py); dict.set_item("textselections", textselections).unwrap(); dict.set_item("expression_index", regexmatch.expression_index()) .unwrap(); @@ -291,7 +294,7 @@ impl PyTextResource { limit: Option, py: Python<'py>, ) -> Bound<'py, PyList> { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|res| { for (i, textselection) in res.split_text(delimiter).enumerate() { list.append(PyTextSelection::from_result_to_py( @@ -697,12 +700,11 @@ impl PyCursor { } } - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (self.cursor == other.cursor).into_py(py), - CompareOp::Ne => (self.cursor != other.cursor).into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => self.cursor == other.cursor, + CompareOp::Ne => self.cursor != other.cursor, + _ => false, } } @@ -797,16 +799,15 @@ impl PyOffset { .ok_or(PyValueError::new_err(format!("Offset has unknown length",))) } - pub(crate) fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + pub(crate) fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (self.offset.begin == other.offset.begin - && self.offset.end == other.offset.end) - .into_py(py), - CompareOp::Ne => (self.offset.begin != other.offset.begin - || self.offset.end != other.offset.end) - .into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => { + self.offset.begin == other.offset.begin && self.offset.end == other.offset.end + } + CompareOp::Ne => { + self.offset.begin != other.offset.begin || self.offset.end != other.offset.end + } + _ => false, } } diff --git a/src/selector.rs b/src/selector.rs index b16d121..932d22d 100644 --- a/src/selector.rs +++ b/src/selector.rs @@ -46,12 +46,11 @@ impl PySelectorKind { kind: SelectorKind::DirectionalSelector, }; - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (*self == *other).into_py(py), - CompareOp::Ne => (*self != *other).into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => *self == *other, + CompareOp::Ne => *self != *other, + _ => false, } } } @@ -496,12 +495,11 @@ impl PySelector { }) } - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (*self == *other).into_py(py), - CompareOp::Ne => (*self != *other).into_py(py), - _ => py.NotImplemented(), + CompareOp::Eq => *self == *other, + CompareOp::Ne => *self != *other, + _ => false, } } } diff --git a/src/substore.rs b/src/substore.rs index f6b2b0e..029a3cd 100644 --- a/src/substore.rs +++ b/src/substore.rs @@ -37,7 +37,10 @@ impl PyAnnotationSubStore { store: Arc>, py: Python<'py>, ) -> Bound<'py, PyAny> { - Self::new(handle, store).into_py(py).into_bound(py) + Self::new(handle, store) + .into_pyobject(py) + .expect("infallible") + .into_any() } } diff --git a/src/textselection.rs b/src/textselection.rs index 3b6a896..9fd5cf3 100644 --- a/src/textselection.rs +++ b/src/textselection.rs @@ -40,6 +40,18 @@ impl PyTextSelection { } } + pub(crate) fn new_py<'py>( + textselection: TextSelection, + resource: TextResourceHandle, + store: Arc>, + py: Python<'py>, + ) -> Bound<'py, PyAny> { + Self::new(textselection, resource, store) + .into_pyobject(py) + .expect("infallible") + .into_any() + } + pub(crate) fn from_result( result: ResultTextSelection<'_>, store: &Arc>, @@ -60,7 +72,10 @@ impl PyTextSelection { store: &Arc>, py: Python<'py>, ) -> Bound<'py, PyAny> { - Self::from_result(result, store).into_py(py).into_bound(py) + Self::from_result(result, store) + .into_pyobject(py) + .expect("infallible") + .into_any() } } @@ -68,7 +83,7 @@ impl PyTextSelection { impl PyTextSelection { /// Resolves a text selection to the actual underlying text fn text<'py>(&self, py: Python<'py>) -> PyResult> { - self.map(|textselection| Ok(PyString::new_bound(py, textselection.text()))) + self.map(|textselection| Ok(PyString::new(py, textselection.text()))) } fn __str__<'py>(&self, py: Python<'py>) -> PyResult> { @@ -85,7 +100,7 @@ impl PyTextSelection { let slice = slice .indices(textselection.textlen().try_into().unwrap()) .expect("expected valid slice"); - Ok(PyString::new_bound( + Ok(PyString::new( py, textselection .text_by_offset(&Offset::simple(slice.start as usize, slice.stop as usize))?, @@ -93,19 +108,20 @@ impl PyTextSelection { }) } - fn __richcmp__(&self, other: PyRef, op: CompareOp) -> Py { - let py = other.py(); + fn __richcmp__(&self, other: PyRef, op: CompareOp) -> bool { match op { - CompareOp::Eq => (self.resource_handle == other.resource_handle - && self.textselection == other.textselection) - .into_py(py), - CompareOp::Ne => (self.resource_handle != other.resource_handle - || self.textselection != other.textselection) - .into_py(py), - CompareOp::Lt => (self.textselection < other.textselection).into_py(py), - CompareOp::Le => (self.textselection <= other.textselection).into_py(py), - CompareOp::Gt => (self.textselection > other.textselection).into_py(py), - CompareOp::Ge => (self.textselection >= other.textselection).into_py(py), + CompareOp::Eq => { + self.resource_handle == other.resource_handle + && self.textselection == other.textselection + } + CompareOp::Ne => { + self.resource_handle != other.resource_handle + || self.textselection != other.textselection + } + CompareOp::Lt => self.textselection < other.textselection, + CompareOp::Le => self.textselection <= other.textselection, + CompareOp::Gt => self.textselection > other.textselection, + CompareOp::Ge => self.textselection >= other.textselection, } } @@ -161,7 +177,7 @@ impl PyTextSelection { case_sensitive: Option, py: Python<'py>, ) -> Bound<'py, PyList> { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|textselection| { if case_sensitive == Some(false) { for (i, textselection) in textselection.find_text_nocase(fragment).enumerate() { @@ -206,7 +222,7 @@ impl PyTextSelection { allow_skip_alphabetic: Option, py: Python<'py>, ) -> Bound<'py, PyList> { - let list: Bound<'py, PyList> = PyList::empty_bound(py); + let list: Bound<'py, PyList> = PyList::empty(py); self.map(|textselection| { let results = textselection.find_text_sequence( &fragments.iter().map(|s| s.as_str()).collect::>(), @@ -243,7 +259,7 @@ impl PyTextSelection { /// You can set `limit` to the max number of elements you want to return. #[pyo3(signature = (delimiter,limit=None))] fn split_text(&self, delimiter: &str, limit: Option, py: Python) -> Py { - let list = PyList::empty_bound(py); + let list = PyList::empty(py); self.map(|textselection| { for (i, textselection) in textselection.split_text(delimiter).enumerate() { list.append(PyTextSelection::from_result_to_py(