Upgrades to PyO3 0.21

pull/839/head
Tpt 9 months ago committed by Thomas Tanon
parent 0f0c1d2742
commit 1d5843fddc
  1. 20
      Cargo.lock
  2. 2
      Cargo.toml
  3. 40
      python/src/dataset.rs
  4. 45
      python/src/io.rs
  5. 2
      python/src/lib.rs
  6. 163
      python/src/model.rs
  7. 36
      python/src/sparql.rs
  8. 161
      python/src/store.rs

20
Cargo.lock generated

@ -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",

@ -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"

@ -30,7 +30,7 @@ pub struct PyDataset {
impl PyDataset {
#[new]
#[pyo3(signature = (quads = None))]
fn new(quads: Option<&PyAny>) -> PyResult<Self> {
fn new(quads: Option<&Bound<'_, PyAny>>) -> PyResult<Self> {
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')))
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
pub fn quads_for_subject(&self, subject: &PyAny) -> PyResult<QuadIter> {
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::<Vec<_>>()
.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')))
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
pub fn quads_for_predicate(&self, predicate: &PyAny) -> PyResult<QuadIter> {
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::<Vec<_>>()
.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')))
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
pub fn quads_for_object(&self, object: &PyAny) -> PyResult<QuadIter> {
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::<Vec<_>>()
.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')))
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
pub fn quads_for_graph_name(&self, graph_name: &PyAny) -> PyResult<QuadIter> {
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::<Vec<_>>()
.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
}
}

@ -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'<http://example.com> <http://example.com/p> "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<PyWritableOutput>,
format: Option<PyRdfFormatInput>,
py: Python<'a>,
) -> PyResult<Option<&'a PyBytes>> {
py: Python<'py>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
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<Self>, Option<PathBuf>) -> PyResult<BufWriter<Self>>,
output: Option<PyWritableOutput>,
py: Python<'_>,
) -> PyResult<Option<&PyBytes>> {
) -> PyResult<Option<Bound<'_, PyBytes>>> {
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<Option<&PyBytes>> {
fn close(self, py: Python<'_>) -> PyResult<Option<Bound<'_, PyBytes>>> {
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::<Bound<'_, PyString>>()?;
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::<usize>()?)
})
}
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::<PyDeprecationWarning>(), message, 0))
Python::with_gil(|py| {
PyErr::warn_bound(py, &py.get_type_bound::<PyDeprecationWarning>(), message, 0)
})
}

@ -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"))?;

@ -93,12 +93,12 @@ impl PyNamedNode {
hash(&self.inner)
}
fn __richcmp__(&self, other: &PyAny, op: CompareOp) -> PyResult<bool> {
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
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<bool> {
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
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<bool> {
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
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<bool> {
fn __richcmp__(&self, other: &Bound<'_, PyAny>, op: CompareOp) -> PyResult<bool> {
if let Ok(other) = other.extract::<PyRef<'_, Self>>() {
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
}
@ -1078,6 +1081,7 @@ impl PyVariable {
}
}
#[derive(FromPyObject)]
pub struct PyNamedNodeRef<'a>(PyRef<'a, PyNamedNode>);
impl<'a> From<&'a PyNamedNodeRef<'a>> for NamedNodeRef<'a> {
@ -1086,21 +1090,7 @@ 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<Self> {
if let Ok(node) = value.extract::<PyRef<'_, PyNamedNode>>() {
Ok(Self(node))
} else {
Err(PyTypeError::new_err(format!(
"{} is not an RDF named node",
value.get_type().name()?,
)))
}
}
}
#[derive(FromPyObject)]
pub enum PyNamedOrBlankNodeRef<'a> {
NamedNode(PyRef<'a, PyNamedNode>),
BlankNode(PyRef<'a, PyBlankNode>),
@ -1115,23 +1105,7 @@ 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<Self> {
if let Ok(node) = value.extract::<PyRef<'_, PyNamedNode>>() {
Ok(Self::NamedNode(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyBlankNode>>() {
Ok(Self::BlankNode(node))
} else {
Err(PyTypeError::new_err(format!(
"{} is not an RDF named or blank node",
value.get_type().name()?,
)))
}
}
}
#[derive(FromPyObject)]
pub enum PySubjectRef<'a> {
NamedNode(PyRef<'a, PyNamedNode>),
BlankNode(PyRef<'a, PyBlankNode>),
@ -1148,25 +1122,7 @@ 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<Self> {
if let Ok(node) = value.extract::<PyRef<'_, PyNamedNode>>() {
Ok(Self::NamedNode(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyBlankNode>>() {
Ok(Self::BlankNode(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyTriple>>() {
Ok(Self::Triple(node))
} else {
Err(PyTypeError::new_err(format!(
"{} is not an RDF named or blank node",
value.get_type().name()?,
)))
}
}
}
#[derive(FromPyObject)]
pub enum PyTermRef<'a> {
NamedNode(PyRef<'a, PyNamedNode>),
BlankNode(PyRef<'a, PyBlankNode>),
@ -1191,31 +1147,11 @@ 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<Self> {
if let Ok(node) = value.extract::<PyRef<'_, PyNamedNode>>() {
Ok(Self::NamedNode(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyBlankNode>>() {
Ok(Self::BlankNode(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyLiteral>>() {
Ok(Self::Literal(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyTriple>>() {
Ok(Self::Triple(node))
} else {
Err(PyTypeError::new_err(format!(
"{} is not an RDF term",
value.get_type().name()?,
)))
}
}
}
#[derive(FromPyObject)]
pub enum PyGraphNameRef<'a> {
NamedNode(PyRef<'a, PyNamedNode>),
BlankNode(PyRef<'a, PyBlankNode>),
DefaultGraph,
DefaultGraph(PyRef<'a, PyDefaultGraph>),
}
impl<'a> From<&'a PyGraphNameRef<'a>> for GraphNameRef<'a> {
@ -1223,32 +1159,7 @@ impl<'a> From<&'a PyGraphNameRef<'a>> for GraphNameRef<'a> {
match value {
PyGraphNameRef::NamedNode(value) => value.inner.as_ref().into(),
PyGraphNameRef::BlankNode(value) => value.inner.as_ref().into(),
PyGraphNameRef::DefaultGraph => Self::DefaultGraph,
}
}
}
impl<'a> From<&'a PyGraphNameRef<'a>> for GraphName {
fn from(value: &'a PyGraphNameRef<'a>) -> Self {
GraphNameRef::from(value).into()
}
}
impl<'a> TryFrom<&'a PyAny> for PyGraphNameRef<'a> {
type Error = PyErr;
fn try_from(value: &'a PyAny) -> PyResult<Self> {
if let Ok(node) = value.extract::<PyRef<'_, PyNamedNode>>() {
Ok(Self::NamedNode(node))
} else if let Ok(node) = value.extract::<PyRef<'_, PyBlankNode>>() {
Ok(Self::BlankNode(node))
} else if value.extract::<PyRef<'_, PyDefaultGraph>>().is_ok() {
Ok(Self::DefaultGraph)
} else {
Err(PyTypeError::new_err(format!(
"{} is not an RDF graph name",
value.get_type().name()?,
)))
PyGraphNameRef::DefaultGraph(_) => Self::DefaultGraph,
}
}
}

@ -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<Query> {
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<PyTerm> {
match key {
fn __getitem__(&self, key: PySolutionKey<'_>) -> PyResult<Option<PyTerm>> {
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<PyWritableOutput>,
format: Option<PyQueryResultsFormatInput>,
py: Python<'a>,
) -> PyResult<Option<&'a PyBytes>> {
py: Python<'py>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
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<PyWritableOutput>,
format: Option<PyQueryResultsFormatInput>,
py: Python<'a>,
) -> PyResult<Option<&'a PyBytes>> {
py: Python<'py>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
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'<http://example.com> <http://example.com/p> "1" .\n'
#[pyo3(signature = (output = None, format = None))]
fn serialize<'a>(
fn serialize<'py>(
&mut self,
output: Option<PyWritableOutput>,
format: Option<PyRdfFormatInput>,
py: Python<'a>,
) -> PyResult<Option<&'a PyBytes>> {
py: Python<'py>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
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
}
}

@ -7,7 +7,7 @@ use crate::io::{
use crate::model::*;
use crate::sparql::*;
use oxigraph::io::RdfParser;
use oxigraph::model::{GraphName, GraphNameRef};
use oxigraph::model::GraphNameRef;
use oxigraph::sparql::Update;
use oxigraph::store::{self, LoaderError, SerializerError, StorageError, Store};
use pyo3::exceptions::{PyRuntimeError, PyValueError};
@ -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)
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
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)
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
#[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::<PyErr, PythonOrStorageError>(
@ -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))
/// [<Quad subject=<NamedNode value=http://example.com> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
#[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<QuadIter> {
let (subject, predicate, object, graph_name) =
extract_quads_pattern(subject, predicate, object, graph_name)?;
Ok(QuadIter {
subject: Option<PySubjectRef<'_>>,
predicate: Option<PyNamedNodeRef<'_>>,
object: Option<PyTermRef<'_>>,
graph_name: Option<PyGraphNameRef<'_>>,
) -> 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 <https://www.w3.org/TR/sparql11-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<PyObject> {
let query = parse_query(
@ -396,6 +395,7 @@ impl PyStore {
/// >>> store.load(input='<foo> <p> "1" .', format=RdfFormat.TURTLE, base_iri="http://example.com/", to_graph=NamedNode("http://example.com/g"))
/// >>> list(store)
/// [<Quad subject=<NamedNode value=http://example.com/foo> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
#[allow(clippy::needless_pass_by_value)]
#[pyo3(signature = (input = None, format = None, *, path = None, base_iri = None, to_graph = None))]
fn load(
&self,
@ -403,14 +403,10 @@ impl PyStore {
format: Option<PyRdfFormatInput>,
path: Option<PathBuf>,
base_iri: Option<&str>,
to_graph: Option<&PyAny>,
to_graph: Option<PyGraphNameRef<'_>>,
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.as_ref().map(GraphNameRef::from);
let input = PyReadable::from_args(&path, input, py)?;
let format = lookup_rdf_format(format, path.as_deref())?;
py.allow_threads(|| {
@ -468,6 +464,7 @@ impl PyStore {
/// >>> store.bulk_load(input=b'<foo> <p> "1" .', format=RdfFormat.TURTLE, base_iri="http://example.com/", to_graph=NamedNode("http://example.com/g"))
/// >>> list(store)
/// [<Quad subject=<NamedNode value=http://example.com/foo> predicate=<NamedNode value=http://example.com/p> object=<Literal value=1 datatype=<NamedNode value=http://www.w3.org/2001/XMLSchema#string>> graph_name=<NamedNode value=http://example.com/g>>]
#[allow(clippy::needless_pass_by_value)]
#[pyo3(signature = (input = None, format = None, *, path = None, base_iri = None, to_graph = None))]
fn bulk_load(
&self,
@ -475,14 +472,10 @@ impl PyStore {
format: Option<PyRdfFormatInput>,
path: Option<PathBuf>,
base_iri: Option<&str>,
to_graph: Option<&PyAny>,
to_graph: Option<PyGraphNameRef<'_>>,
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.as_ref().map(GraphNameRef::from);
let input = PyReadable::from_args(&path, input, py)?;
let format = lookup_rdf_format(format, path.as_deref())?;
py.allow_threads(|| {
@ -539,24 +532,21 @@ impl PyStore {
/// >>> store.dump(output, RdfFormat.TURTLE, from_graph=NamedNode("http://example.com/g"))
/// >>> output.getvalue()
/// b'<http://example.com> <http://example.com/p> "1" .\n'
#[allow(clippy::needless_pass_by_value)]
#[pyo3(signature = (output = None, format = None, *, from_graph = None))]
fn dump<'a>(
fn dump<'py>(
&self,
output: Option<PyWritableOutput>,
format: Option<PyRdfFormatInput>,
from_graph: Option<&PyAny>,
py: Python<'a>,
) -> PyResult<Option<&'a PyBytes>> {
let from_graph_name = if let Some(graph_name) = from_graph {
Some(GraphName::from(&PyGraphNameRef::try_from(graph_name)?))
} else {
None
};
from_graph: Option<PyGraphNameRef<'_>>,
py: Python<'py>,
) -> PyResult<Option<Bound<'py, PyBytes>>> {
let from_graph_name = from_graph.as_ref().map(GraphNameRef::from);
PyWritable::do_write(
|output, file_path| {
py.allow_threads(|| {
let format = lookup_rdf_format(format, file_path.as_deref())?;
if let Some(from_graph_name) = &from_graph_name {
if let Some(from_graph_name) = from_graph_name {
self.inner
.dump_graph_to_write(from_graph_name, format, output)
} else {
@ -597,14 +587,21 @@ 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<bool> {
let graph_name = GraphName::from(&PyGraphNameRef::try_from(graph_name)?);
match graph_name {
GraphName::DefaultGraph => Ok(true),
GraphName::NamedNode(graph_name) => self.inner.contains_named_graph(&graph_name),
GraphName::BlankNode(graph_name) => self.inner.contains_named_graph(&graph_name),
}
.map_err(map_storage_error)
#[allow(clippy::needless_pass_by_value)]
fn contains_named_graph(
&self,
graph_name: PyGraphNameRef<'_>,
py: Python<'_>,
) -> PyResult<bool> {
let graph_name = GraphNameRef::from(&graph_name);
py.allow_threads(|| {
match graph_name {
GraphNameRef::DefaultGraph => Ok(true),
GraphNameRef::NamedNode(graph_name) => self.inner.contains_named_graph(graph_name),
GraphNameRef::BlankNode(graph_name) => self.inner.contains_named_graph(graph_name),
}
.map_err(map_storage_error)
})
}
/// Adds a named graph to the store.
@ -618,16 +615,17 @@ impl PyStore {
/// >>> store.add_graph(NamedNode('http://example.com/g'))
/// >>> list(store.named_graphs())
/// [<NamedNode value=http://example.com/g>]
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 = GraphNameRef::from(&graph_name);
py.allow_threads(|| {
match graph_name {
GraphName::DefaultGraph => Ok(()),
GraphName::NamedNode(graph_name) => {
self.inner.insert_named_graph(&graph_name).map(|_| ())
GraphNameRef::DefaultGraph => Ok(()),
GraphNameRef::NamedNode(graph_name) => {
self.inner.insert_named_graph(graph_name).map(|_| ())
}
GraphName::BlankNode(graph_name) => {
self.inner.insert_named_graph(&graph_name).map(|_| ())
GraphNameRef::BlankNode(graph_name) => {
self.inner.insert_named_graph(graph_name).map(|_| ())
}
}
.map_err(map_storage_error)
@ -648,11 +646,12 @@ impl PyStore {
/// []
/// >>> list(store.named_graphs())
/// [<NamedNode value=http://example.com/g>]
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 = GraphNameRef::from(&graph_name);
py.allow_threads(|| {
self.inner
.clear_graph(&graph_name)
.clear_graph(graph_name)
.map_err(map_storage_error)
})
}
@ -671,16 +670,17 @@ 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 = GraphNameRef::from(&graph_name);
py.allow_threads(|| {
match graph_name {
GraphName::DefaultGraph => self.inner.clear_graph(GraphNameRef::DefaultGraph),
GraphName::NamedNode(graph_name) => {
self.inner.remove_named_graph(&graph_name).map(|_| ())
GraphNameRef::DefaultGraph => self.inner.clear_graph(GraphNameRef::DefaultGraph),
GraphNameRef::NamedNode(graph_name) => {
self.inner.remove_named_graph(graph_name).map(|_| ())
}
GraphName::BlankNode(graph_name) => {
self.inner.remove_named_graph(&graph_name).map(|_| ())
GraphNameRef::BlankNode(graph_name) => {
self.inner.remove_named_graph(graph_name).map(|_| ())
}
}
.map_err(map_storage_error)
@ -816,45 +816,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<PySubjectRef<'a>>,
Option<PyNamedNodeRef<'a>>,
Option<PyTermRef<'a>>,
Option<PyGraphNameRef<'a>>,
)> {
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(),

Loading…
Cancel
Save