From c1dba9803856501cf5f24cbbdfa3c8d134300961 Mon Sep 17 00:00:00 2001 From: Tpt Date: Tue, 12 Mar 2024 21:21:26 +0100 Subject: [PATCH] Upgrades to PyO3 0.21 --- Cargo.lock | 20 +++---- Cargo.toml | 2 +- python/src/dataset.rs | 40 +++++++------- python/src/io.rs | 45 +++++++++------- python/src/lib.rs | 2 +- python/src/model.rs | 123 ++++++++++++++++++++---------------------- python/src/sparql.rs | 36 ++++++------- python/src/store.rs | 114 ++++++++++++--------------------------- 8 files changed, 169 insertions(+), 213 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 92afef79..c6dd1ad9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1366,9 +1366,9 @@ dependencies = [ [[package]] name = "pyo3" -version = "0.20.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53bdbb96d49157e65d45cc287af5f32ffadd5f4761438b527b055fb0d4bb8233" +checksum = "a02a88a17e74cadbc8ce77855e1d6c8ad0ab82901a4a9b5046bd01c1c0bd95cd" dependencies = [ "cfg-if", "indoc", @@ -1384,9 +1384,9 @@ dependencies = [ [[package]] name = "pyo3-build-config" -version = "0.20.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deaa5745de3f5231ce10517a1f5dd97d53e5a2fd77aa6b5842292085831d48d7" +checksum = "a5eb0b6ecba38961f6f4bd6cd5906dfab3cd426ff37b2eed5771006aa31656f1" dependencies = [ "once_cell", "target-lexicon", @@ -1394,9 +1394,9 @@ dependencies = [ [[package]] name = "pyo3-ffi" -version = "0.20.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b42531d03e08d4ef1f6e85a2ed422eb678b8cd62b762e53891c05faf0d4afa" +checksum = "ba8a6e48a29b5d22e4fdaf132d8ba8d3203ee9f06362d48f244346902a594ec3" dependencies = [ "libc", "pyo3-build-config", @@ -1404,9 +1404,9 @@ dependencies = [ [[package]] name = "pyo3-macros" -version = "0.20.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7305c720fa01b8055ec95e484a6eca7a83c841267f0dd5280f0c8b8551d2c158" +checksum = "4e80493c5965f94a747d0782a607b2328a4eea5391327b152b00e2f3b001cede" dependencies = [ "proc-macro2", "pyo3-macros-backend", @@ -1416,9 +1416,9 @@ dependencies = [ [[package]] name = "pyo3-macros-backend" -version = "0.20.3" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c7e9b68bb9c3149c5b0cade5d07f953d6d125eb4337723c4ccdb665f1f96185" +checksum = "fcd7d86f42004025200e12a6a8119bd878329e6fddef8178eaafa4e4b5906c5b" dependencies = [ "heck 0.4.1", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index cc7f40b8..57ca2c4c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -51,7 +51,7 @@ oxiri = "0.2.3" peg = "0.8" pkg-config = "0.3.25" predicates = ">=2.0, <4.0" -pyo3 = "0.20.1" +pyo3 = "0.21.0" quick-xml = ">=0.29, <0.32" rand = "0.8" rayon-core = "1.11" diff --git a/python/src/dataset.rs b/python/src/dataset.rs index 1ef0e7a6..25eb729e 100644 --- a/python/src/dataset.rs +++ b/python/src/dataset.rs @@ -30,7 +30,7 @@ pub struct PyDataset { impl PyDataset { #[new] #[pyo3(signature = (quads = None))] - fn new(quads: Option<&PyAny>) -> PyResult { + fn new(quads: Option<&Bound<'_, PyAny>>) -> PyResult { let mut inner = Dataset::new(); if let Some(quads) = quads { for quad in quads.iter()? { @@ -50,15 +50,16 @@ impl PyDataset { /// >>> store = Dataset([Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))]) /// >>> list(store.quads_for_subject(NamedNode('http://example.com'))) /// [ predicate= object=> graph_name=>] - pub fn quads_for_subject(&self, subject: &PyAny) -> PyResult { - Ok(QuadIter { + #[allow(clippy::needless_pass_by_value)] + pub fn quads_for_subject(&self, subject: PySubjectRef<'_>) -> QuadIter { + QuadIter { inner: self .inner - .quads_for_subject(&PySubjectRef::try_from(subject)?) + .quads_for_subject(&subject) .map(QuadRef::into_owned) .collect::>() .into_iter(), - }) + } } /// Looks for the quads with the given predicate. @@ -71,15 +72,16 @@ impl PyDataset { /// >>> store = Dataset([Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))]) /// >>> list(store.quads_for_predicate(NamedNode('http://example.com/p'))) /// [ predicate= object=> graph_name=>] - pub fn quads_for_predicate(&self, predicate: &PyAny) -> PyResult { - Ok(QuadIter { + #[allow(clippy::needless_pass_by_value)] + pub fn quads_for_predicate(&self, predicate: PyNamedNodeRef<'_>) -> QuadIter { + QuadIter { inner: self .inner - .quads_for_predicate(&PyNamedNodeRef::try_from(predicate)?) + .quads_for_predicate(&predicate) .map(QuadRef::into_owned) .collect::>() .into_iter(), - }) + } } /// Looks for the quads with the given object. @@ -92,15 +94,16 @@ impl PyDataset { /// >>> store = Dataset([Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))]) /// >>> list(store.quads_for_object(Literal('1'))) /// [ predicate= object=> graph_name=>] - pub fn quads_for_object(&self, object: &PyAny) -> PyResult { - Ok(QuadIter { + #[allow(clippy::needless_pass_by_value)] + pub fn quads_for_object(&self, object: PyTermRef<'_>) -> QuadIter { + QuadIter { inner: self .inner - .quads_for_object(&PyTermRef::try_from(object)?) + .quads_for_object(&object) .map(QuadRef::into_owned) .collect::>() .into_iter(), - }) + } } /// Looks for the quads with the given graph name. @@ -113,15 +116,16 @@ impl PyDataset { /// >>> store = Dataset([Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))]) /// >>> list(store.quads_for_graph_name(NamedNode('http://example.com/g'))) /// [ predicate= object=> graph_name=>] - pub fn quads_for_graph_name(&self, graph_name: &PyAny) -> PyResult { - Ok(QuadIter { + #[allow(clippy::needless_pass_by_value)] + pub fn quads_for_graph_name(&self, graph_name: PyGraphNameRef<'_>) -> QuadIter { + QuadIter { inner: self .inner - .quads_for_graph_name(&PyGraphNameRef::try_from(graph_name)?) + .quads_for_graph_name(&graph_name) .map(QuadRef::into_owned) .collect::>() .into_iter(), - }) + } } /// Adds a quad to the dataset. @@ -317,7 +321,7 @@ impl PyCanonicalizationAlgorithm { /// :type memo: typing.Any /// :rtype: CanonicalizationAlgorithm #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } } diff --git a/python/src/io.rs b/python/src/io.rs index c778da30..d03adeb6 100644 --- a/python/src/io.rs +++ b/python/src/io.rs @@ -6,7 +6,7 @@ use oxigraph::model::QuadRef; use pyo3::exceptions::{PyDeprecationWarning, PySyntaxError, PyValueError}; use pyo3::intern; use pyo3::prelude::*; -use pyo3::types::PyBytes; +use pyo3::types::{PyBytes, PyString}; use std::cmp::max; use std::ffi::OsStr; use std::fs::File; @@ -118,12 +118,12 @@ pub fn parse( /// b' "1" .\n' #[pyfunction] #[pyo3(signature = (input, output = None, format = None))] -pub fn serialize<'a>( - input: &PyAny, +pub fn serialize<'py>( + input: &Bound<'py, PyAny>, output: Option, format: Option, - py: Python<'a>, -) -> PyResult> { + py: Python<'py>, +) -> PyResult>> { PyWritable::do_write( |output, file_path| { let format = lookup_rdf_format(format, file_path.as_deref())?; @@ -355,7 +355,7 @@ impl PyRdfFormat { /// :type memo: typing.Any /// :rtype: RdfFormat #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } } @@ -423,7 +423,7 @@ impl PyWritable { write: impl FnOnce(BufWriter, Option) -> PyResult>, output: Option, py: Python<'_>, - ) -> PyResult> { + ) -> PyResult>> { let (output, file_path) = match output { Some(PyWritableOutput::Path(file_path)) => ( Self::File(py.allow_threads(|| File::create(&file_path))?), @@ -436,9 +436,9 @@ impl PyWritable { py.allow_threads(|| writer.into_inner())?.close(py) } - fn close(self, py: Python<'_>) -> PyResult> { + fn close(self, py: Python<'_>) -> PyResult>> { match self { - Self::Bytes(bytes) => Ok(Some(PyBytes::new(py, &bytes))), + Self::Bytes(bytes) => Ok(Some(PyBytes::new_bound(py, &bytes))), Self::File(mut file) => { py.allow_threads(|| { file.flush()?; @@ -489,13 +489,18 @@ impl Read for PyIo { let to_read = max(1, buf.len() / 4); // We divide by 4 because TextIO works with number of characters and not with number of bytes let read = self .0 - .as_ref(py) + .bind(py) .call_method1(intern!(py, "read"), (to_read,))?; - let bytes = read - .extract::<&[u8]>() - .or_else(|_| read.extract::<&str>().map(str::as_bytes))?; - buf[..bytes.len()].copy_from_slice(bytes); - Ok(bytes.len()) + Ok(if let Ok(bytes) = read.extract::<&[u8]>() { + buf[..bytes.len()].copy_from_slice(bytes); + bytes.len() + } else { + // TODO: Python 3.10+ use directly .extract<&str> + let string = read.extract::>()?; + let str = string.to_cow()?; + buf[..str.len()].copy_from_slice(str.as_bytes()); + str.len() + }) }) } } @@ -505,15 +510,15 @@ impl Write for PyIo { Python::with_gil(|py| { Ok(self .0 - .as_ref(py) - .call_method1(intern!(py, "write"), (PyBytes::new(py, buf),))? + .bind(py) + .call_method1(intern!(py, "write"), (PyBytes::new_bound(py, buf),))? .extract::()?) }) } fn flush(&mut self) -> io::Result<()> { Python::with_gil(|py| { - self.0.as_ref(py).call_method0(intern!(py, "flush"))?; + self.0.bind(py).call_method0(intern!(py, "flush"))?; Ok(()) }) } @@ -629,5 +634,7 @@ pub fn python_version() -> (u8, u8) { } pub fn deprecation_warning(message: &str) -> PyResult<()> { - Python::with_gil(|py| PyErr::warn(py, py.get_type::(), message, 0)) + Python::with_gil(|py| { + PyErr::warn_bound(py, &py.get_type_bound::(), message, 0) + }) } diff --git a/python/src/lib.rs b/python/src/lib.rs index 9cbf780c..89e3d502 100644 --- a/python/src/lib.rs +++ b/python/src/lib.rs @@ -19,7 +19,7 @@ use pyo3::prelude::*; /// Oxigraph Python bindings #[pymodule] -fn pyoxigraph(_py: Python<'_>, module: &PyModule) -> PyResult<()> { +fn pyoxigraph(_py: Python<'_>, module: &Bound<'_, PyModule>) -> PyResult<()> { module.add("__package__", "pyoxigraph")?; module.add("__version__", env!("CARGO_PKG_VERSION"))?; module.add("__author__", env!("CARGO_PKG_AUTHORS").replace(':', "\n"))?; diff --git a/python/src/model.rs b/python/src/model.rs index 451f1cb0..7e578b80 100644 --- a/python/src/model.rs +++ b/python/src/model.rs @@ -93,12 +93,12 @@ impl PyNamedNode { hash(&self.inner) } - fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyResult { + fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult { if let Ok(other) = other.extract::>() { Ok(op.matches(self.cmp(&other))) - } else if PyBlankNode::is_type_of(other) - || PyLiteral::is_type_of(other) - || PyDefaultGraph::is_type_of(other) + } else if PyBlankNode::is_type_of_bound(other) + || PyLiteral::is_type_of_bound(other) + || PyDefaultGraph::is_type_of_bound(other) { eq_compare_other_type(op) } else { @@ -121,7 +121,7 @@ impl PyNamedNode { /// :type memo: typing.Any /// :rtype: NamedNode #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } @@ -220,12 +220,12 @@ impl PyBlankNode { hash(&self.inner) } - fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyResult { + fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult { if let Ok(other) = other.extract::>() { eq_compare(self, &other, op) - } else if PyNamedNode::is_type_of(other) - || PyLiteral::is_type_of(other) - || PyDefaultGraph::is_type_of(other) + } else if PyNamedNode::is_type_of_bound(other) + || PyLiteral::is_type_of_bound(other) + || PyDefaultGraph::is_type_of_bound(other) { eq_compare_other_type(op) } else { @@ -248,7 +248,7 @@ impl PyBlankNode { /// :type memo: typing.Any /// :rtype: BlankNode #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } @@ -376,12 +376,12 @@ impl PyLiteral { hash(&self.inner) } - fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyResult { + fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult { if let Ok(other) = other.extract::>() { eq_compare(self, &other, op) - } else if PyNamedNode::is_type_of(other) - || PyBlankNode::is_type_of(other) - || PyDefaultGraph::is_type_of(other) + } else if PyNamedNode::is_type_of_bound(other) + || PyBlankNode::is_type_of_bound(other) + || PyDefaultGraph::is_type_of_bound(other) { eq_compare_other_type(op) } else { @@ -392,8 +392,11 @@ impl PyLiteral { } /// :rtype: typing.Any - fn __getnewargs_ex__<'a>(&'a self, py: Python<'a>) -> PyResult<((&'a str,), &'a PyDict)> { - let kwargs = PyDict::new(py); + fn __getnewargs_ex__<'a, 'py>( + &'a self, + py: Python<'py>, + ) -> PyResult<((&'a str,), Bound<'py, PyDict>)> { + let kwargs = PyDict::new_bound(py); if let Some(language) = self.language() { kwargs.set_item("language", language)?; } else { @@ -410,7 +413,7 @@ impl PyLiteral { /// :type memo: typing.Any /// :rtype: Literal #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } @@ -450,12 +453,12 @@ impl PyDefaultGraph { 0 } - fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyResult { + fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult { if let Ok(other) = other.extract::>() { eq_compare(self, &other, op) - } else if PyNamedNode::is_type_of(other) - || PyBlankNode::is_type_of(other) - || PyLiteral::is_type_of(other) + } else if PyNamedNode::is_type_of_bound(other) + || PyBlankNode::is_type_of_bound(other) + || PyLiteral::is_type_of_bound(other) { eq_compare_other_type(op) } else { @@ -466,8 +469,8 @@ impl PyDefaultGraph { } /// :rtype: typing.Any - fn __getnewargs__<'a>(&self, py: Python<'a>) -> &'a PyTuple { - PyTuple::empty(py) + fn __getnewargs__<'py>(&self, py: Python<'py>) -> Bound<'py, PyTuple> { + PyTuple::empty_bound(py) } /// :rtype: DefaultGraph @@ -478,7 +481,7 @@ impl PyDefaultGraph { /// :type memo: typing.Any /// :rtype: DefaultGraph #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } } @@ -739,7 +742,7 @@ impl PyTriple { /// :type memo: typing.Any /// :rtype: Triple #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } @@ -976,7 +979,7 @@ impl PyQuad { /// :type memo: typing.Any /// :rtype: Quad #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } @@ -1068,7 +1071,7 @@ impl PyVariable { /// :type memo: typing.Any /// :rtype: Variable #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } @@ -1086,16 +1089,14 @@ impl<'a> From<&'a PyNamedNodeRef<'a>> for NamedNodeRef<'a> { } } -impl<'a> TryFrom<&'a PyAny> for PyNamedNodeRef<'a> { - type Error = PyErr; - - fn try_from(value: &'a PyAny) -> PyResult { - if let Ok(node) = value.extract::>() { +impl<'py> FromPyObject<'py> for PyNamedNodeRef<'py> { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + if let Ok(node) = ob.extract() { Ok(Self(node)) } else { Err(PyTypeError::new_err(format!( "{} is not an RDF named node", - value.get_type().name()?, + ob.get_type().name()?, ))) } } @@ -1115,18 +1116,16 @@ impl<'a> From<&'a PyNamedOrBlankNodeRef<'a>> for NamedOrBlankNodeRef<'a> { } } -impl<'a> TryFrom<&'a PyAny> for PyNamedOrBlankNodeRef<'a> { - type Error = PyErr; - - fn try_from(value: &'a PyAny) -> PyResult { - if let Ok(node) = value.extract::>() { +impl<'py> FromPyObject<'py> for PyNamedOrBlankNodeRef<'py> { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + if let Ok(node) = ob.extract::>() { Ok(Self::NamedNode(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::BlankNode(node)) } else { Err(PyTypeError::new_err(format!( "{} is not an RDF named or blank node", - value.get_type().name()?, + ob.get_type().name()?, ))) } } @@ -1148,20 +1147,18 @@ impl<'a> From<&'a PySubjectRef<'a>> for SubjectRef<'a> { } } -impl<'a> TryFrom<&'a PyAny> for PySubjectRef<'a> { - type Error = PyErr; - - fn try_from(value: &'a PyAny) -> PyResult { - if let Ok(node) = value.extract::>() { +impl<'py> FromPyObject<'py> for PySubjectRef<'py> { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + if let Ok(node) = ob.extract::>() { Ok(Self::NamedNode(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::BlankNode(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::Triple(node)) } else { Err(PyTypeError::new_err(format!( "{} is not an RDF named or blank node", - value.get_type().name()?, + ob.get_type().name()?, ))) } } @@ -1191,22 +1188,20 @@ impl<'a> From<&'a PyTermRef<'a>> for Term { } } -impl<'a> TryFrom<&'a PyAny> for PyTermRef<'a> { - type Error = PyErr; - - fn try_from(value: &'a PyAny) -> PyResult { - if let Ok(node) = value.extract::>() { +impl<'py> FromPyObject<'py> for PyTermRef<'py> { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + if let Ok(node) = ob.extract::>() { Ok(Self::NamedNode(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::BlankNode(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::Literal(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::Triple(node)) } else { Err(PyTypeError::new_err(format!( "{} is not an RDF term", - value.get_type().name()?, + ob.get_type().name()?, ))) } } @@ -1234,20 +1229,18 @@ impl<'a> From<&'a PyGraphNameRef<'a>> for GraphName { } } -impl<'a> TryFrom<&'a PyAny> for PyGraphNameRef<'a> { - type Error = PyErr; - - fn try_from(value: &'a PyAny) -> PyResult { - if let Ok(node) = value.extract::>() { +impl<'py> FromPyObject<'py> for PyGraphNameRef<'py> { + fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult { + if let Ok(node) = ob.extract::>() { Ok(Self::NamedNode(node)) - } else if let Ok(node) = value.extract::>() { + } else if let Ok(node) = ob.extract::>() { Ok(Self::BlankNode(node)) - } else if value.extract::>().is_ok() { + } else if ob.extract::>().is_ok() { Ok(Self::DefaultGraph) } else { Err(PyTypeError::new_err(format!( "{} is not an RDF graph name", - value.get_type().name()?, + ob.get_type().name()?, ))) } } diff --git a/python/src/sparql.rs b/python/src/sparql.rs index 86ee7804..f2a40c85 100644 --- a/python/src/sparql.rs +++ b/python/src/sparql.rs @@ -14,7 +14,7 @@ use oxigraph::sparql::{ use pyo3::basic::CompareOp; use pyo3::exceptions::{PyRuntimeError, PySyntaxError, PyValueError}; use pyo3::prelude::*; -use pyo3::types::PyBytes; +use pyo3::types::{PyBytes, PyString}; use std::ffi::OsStr; use std::io; use std::path::{Path, PathBuf}; @@ -24,8 +24,8 @@ pub fn parse_query( query: &str, base_iri: Option<&str>, use_default_graph_as_union: bool, - default_graph: Option<&PyAny>, - named_graphs: Option<&PyAny>, + default_graph: Option<&Bound<'_, PyAny>>, + named_graphs: Option<&Bound<'_, PyAny>>, py: Python<'_>, ) -> PyResult { let mut query = allow_threads_unsafe(py, || Query::parse(query, base_iri)) @@ -133,13 +133,13 @@ impl PyQuerySolution { self.inner.len() } - fn __getitem__(&self, key: PySolutionKey<'_>) -> Option { - match key { + fn __getitem__(&self, key: PySolutionKey<'_>) -> PyResult> { + Ok(match key { PySolutionKey::Usize(key) => self.inner.get(key), - PySolutionKey::Str(key) => self.inner.get(key), + PySolutionKey::Str(key) => self.inner.get(key.to_cow()?.as_ref()), PySolutionKey::Variable(key) => self.inner.get(<&Variable>::from(&*key)), } - .map(|term| PyTerm::from(term.clone())) + .map(|term| PyTerm::from(term.clone()))) } #[allow(clippy::unnecessary_to_owned)] @@ -153,7 +153,7 @@ impl PyQuerySolution { #[derive(FromPyObject)] pub enum PySolutionKey<'a> { Usize(usize), - Str(&'a str), + Str(Bound<'a, PyString>), // TODO: Python 3.10+: use &str Variable(PyRef<'a, PyVariable>), } @@ -237,12 +237,12 @@ impl PyQuerySolutions { /// >>> results.serialize(format=QueryResultsFormat.JSON) /// b'{"head":{"vars":["s","p","o"]},"results":{"bindings":[{"s":{"type":"uri","value":"http://example.com"},"p":{"type":"uri","value":"http://example.com/p"},"o":{"type":"literal","value":"1"}}]}}' #[pyo3(signature = (output = None, format = None))] - fn serialize<'a>( + fn serialize<'py>( &mut self, output: Option, format: Option, - py: Python<'a>, - ) -> PyResult> { + py: Python<'py>, + ) -> PyResult>> { PyWritable::do_write( |output, file_path| { let format = lookup_query_results_format(format, file_path.as_deref())?; @@ -337,12 +337,12 @@ impl PyQueryBoolean { /// >>> results.serialize(format=QueryResultsFormat.JSON) /// b'{"head":{},"boolean":true}' #[pyo3(signature = (output = None, format = None))] - fn serialize<'a>( + fn serialize<'py>( &mut self, output: Option, format: Option, - py: Python<'a>, - ) -> PyResult> { + py: Python<'py>, + ) -> PyResult>> { PyWritable::do_write( |output, file_path| { let format = lookup_query_results_format(format, file_path.as_deref())?; @@ -415,12 +415,12 @@ impl PyQueryTriples { /// >>> results.serialize(format=RdfFormat.N_TRIPLES) /// b' "1" .\n' #[pyo3(signature = (output = None, format = None))] - fn serialize<'a>( + fn serialize<'py>( &mut self, output: Option, format: Option, - py: Python<'a>, - ) -> PyResult> { + py: Python<'py>, + ) -> PyResult>> { PyWritable::do_write( |output, file_path| { let format = lookup_rdf_format(format, file_path.as_deref())?; @@ -642,7 +642,7 @@ impl PyQueryResultsFormat { /// :type memo: typing.Any /// :rtype: QueryResultsFormat #[allow(unused_variables)] - fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ PyAny) -> PyRef<'a, Self> { + fn __deepcopy__<'a>(slf: PyRef<'a, Self>, memo: &'_ Bound<'_, PyAny>) -> PyRef<'a, Self> { slf } } diff --git a/python/src/store.rs b/python/src/store.rs index a810559f..b4f83249 100644 --- a/python/src/store.rs +++ b/python/src/store.rs @@ -161,7 +161,7 @@ impl PyStore { /// >>> store.extend([Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))]) /// >>> list(store) /// [ predicate= object=> graph_name=>] - fn extend(&self, quads: &PyAny, py: Python<'_>) -> PyResult<()> { + fn extend(&self, quads: &Bound<'_, PyAny>, py: Python<'_>) -> PyResult<()> { let quads = quads .iter()? .map(|q| q?.extract()) @@ -187,7 +187,7 @@ impl PyStore { /// >>> list(store) /// [ predicate= object=> graph_name=>] #[cfg(not(target_family = "wasm"))] - fn bulk_extend(&self, quads: &PyAny) -> PyResult<()> { + fn bulk_extend(&self, quads: &Bound<'_, PyAny>) -> PyResult<()> { self.inner .bulk_loader() .load_ok_quads::( @@ -234,24 +234,23 @@ impl PyStore { /// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))) /// >>> list(store.quads_for_pattern(NamedNode('http://example.com'), None, None, None)) /// [ predicate= object=> graph_name=>] + #[allow(clippy::needless_pass_by_value)] #[pyo3(signature = (subject, predicate, object, graph_name = None))] fn quads_for_pattern( &self, - subject: &PyAny, - predicate: &PyAny, - object: &PyAny, - graph_name: Option<&PyAny>, - ) -> PyResult { - let (subject, predicate, object, graph_name) = - extract_quads_pattern(subject, predicate, object, graph_name)?; - Ok(QuadIter { + subject: Option>, + predicate: Option>, + object: Option>, + graph_name: Option>, + ) -> QuadIter { + QuadIter { inner: self.inner.quads_for_pattern( subject.as_ref().map(Into::into), predicate.as_ref().map(Into::into), object.as_ref().map(Into::into), graph_name.as_ref().map(Into::into), ), - }) + } } /// Executes a `SPARQL 1.1 query `_. @@ -297,8 +296,8 @@ impl PyStore { query: &str, base_iri: Option<&str>, use_default_graph_as_union: bool, - default_graph: Option<&PyAny>, - named_graphs: Option<&PyAny>, + default_graph: Option<&Bound<'_, PyAny>>, + named_graphs: Option<&Bound<'_, PyAny>>, py: Python<'_>, ) -> PyResult { let query = parse_query( @@ -403,14 +402,10 @@ impl PyStore { format: Option, path: Option, base_iri: Option<&str>, - to_graph: Option<&PyAny>, + to_graph: Option>, py: Python<'_>, ) -> PyResult<()> { - let to_graph_name = if let Some(graph_name) = to_graph { - Some(GraphName::from(&PyGraphNameRef::try_from(graph_name)?)) - } else { - None - }; + let to_graph_name = to_graph.map(|graph_name| GraphName::from(&graph_name)); let input = PyReadable::from_args(&path, input, py)?; let format = lookup_rdf_format(format, path.as_deref())?; py.allow_threads(|| { @@ -475,14 +470,10 @@ impl PyStore { format: Option, path: Option, base_iri: Option<&str>, - to_graph: Option<&PyAny>, + to_graph: Option>, py: Python<'_>, ) -> PyResult<()> { - let to_graph_name = if let Some(graph_name) = to_graph { - Some(GraphName::from(&PyGraphNameRef::try_from(graph_name)?)) - } else { - None - }; + let to_graph_name = to_graph.map(|graph_name| GraphName::from(&graph_name)); let input = PyReadable::from_args(&path, input, py)?; let format = lookup_rdf_format(format, path.as_deref())?; py.allow_threads(|| { @@ -540,18 +531,14 @@ impl PyStore { /// >>> output.getvalue() /// b' "1" .\n' #[pyo3(signature = (output = None, format = None, *, from_graph = None))] - fn dump<'a>( + fn dump<'py>( &self, output: Option, format: Option, - from_graph: Option<&PyAny>, - py: Python<'a>, - ) -> PyResult> { - let from_graph_name = if let Some(graph_name) = from_graph { - Some(GraphName::from(&PyGraphNameRef::try_from(graph_name)?)) - } else { - None - }; + from_graph: Option>, + py: Python<'py>, + ) -> PyResult>> { + let from_graph_name = from_graph.map(|graph_name| GraphName::from(&graph_name)); PyWritable::do_write( |output, file_path| { py.allow_threads(|| { @@ -597,8 +584,9 @@ impl PyStore { /// >>> store.add_graph(NamedNode('http://example.com/g')) /// >>> store.contains_named_graph(NamedNode('http://example.com/g')) /// True - fn contains_named_graph(&self, graph_name: &PyAny) -> PyResult { - let graph_name = GraphName::from(&PyGraphNameRef::try_from(graph_name)?); + #[allow(clippy::needless_pass_by_value)] + fn contains_named_graph(&self, graph_name: PyGraphNameRef<'_>) -> PyResult { + let graph_name = GraphName::from(&graph_name); match graph_name { GraphName::DefaultGraph => Ok(true), GraphName::NamedNode(graph_name) => self.inner.contains_named_graph(&graph_name), @@ -618,8 +606,9 @@ impl PyStore { /// >>> store.add_graph(NamedNode('http://example.com/g')) /// >>> list(store.named_graphs()) /// [] - fn add_graph(&self, graph_name: &PyAny, py: Python<'_>) -> PyResult<()> { - let graph_name = GraphName::from(&PyGraphNameRef::try_from(graph_name)?); + #[allow(clippy::needless_pass_by_value)] + fn add_graph(&self, graph_name: PyGraphNameRef<'_>, py: Python<'_>) -> PyResult<()> { + let graph_name = GraphName::from(&graph_name); py.allow_threads(|| { match graph_name { GraphName::DefaultGraph => Ok(()), @@ -648,8 +637,9 @@ impl PyStore { /// [] /// >>> list(store.named_graphs()) /// [] - fn clear_graph(&self, graph_name: &PyAny, py: Python<'_>) -> PyResult<()> { - let graph_name = GraphName::from(&PyGraphNameRef::try_from(graph_name)?); + #[allow(clippy::needless_pass_by_value)] + fn clear_graph(&self, graph_name: PyGraphNameRef<'_>, py: Python<'_>) -> PyResult<()> { + let graph_name = GraphName::from(&graph_name); py.allow_threads(|| { self.inner .clear_graph(&graph_name) @@ -671,8 +661,9 @@ impl PyStore { /// >>> store.remove_graph(NamedNode('http://example.com/g')) /// >>> list(store.named_graphs()) /// [] - fn remove_graph(&self, graph_name: &PyAny, py: Python<'_>) -> PyResult<()> { - let graph_name = GraphName::from(&PyGraphNameRef::try_from(graph_name)?); + #[allow(clippy::needless_pass_by_value)] + fn remove_graph(&self, graph_name: PyGraphNameRef<'_>, py: Python<'_>) -> PyResult<()> { + let graph_name = GraphName::from(&graph_name); py.allow_threads(|| { match graph_name { GraphName::DefaultGraph => self.inner.clear_graph(GraphNameRef::DefaultGraph), @@ -816,45 +807,6 @@ impl GraphNameIter { } } -pub fn extract_quads_pattern<'a>( - subject: &'a PyAny, - predicate: &'a PyAny, - object: &'a PyAny, - graph_name: Option<&'a PyAny>, -) -> PyResult<( - Option>, - Option>, - Option>, - Option>, -)> { - Ok(( - if subject.is_none() { - None - } else { - Some(TryFrom::try_from(subject)?) - }, - if predicate.is_none() { - None - } else { - Some(TryFrom::try_from(predicate)?) - }, - if object.is_none() { - None - } else { - Some(TryFrom::try_from(object)?) - }, - if let Some(graph_name) = graph_name { - if graph_name.is_none() { - None - } else { - Some(TryFrom::try_from(graph_name)?) - } - } else { - None - }, - )) -} - pub fn map_storage_error(error: StorageError) -> PyErr { match error { StorageError::Io(error) => error.into(),