Improves pyoxigraph documentation

pull/173/head
Tpt 3 years ago
parent ad4dd2832e
commit d6d88b074c
  1. 7
      lib/src/store.rs
  2. 5
      python/docs/index.rst
  3. 22
      python/src/io.rs
  4. 74
      python/src/model.rs
  5. 3
      python/src/sparql.rs
  6. 171
      python/src/store.rs

@ -45,8 +45,9 @@ use std::{fmt, io, str};
/// Allows to query and update it using SPARQL. /// Allows to query and update it using SPARQL.
/// It is based on the [RocksDB](https://rocksdb.org/) key-value store. /// It is based on the [RocksDB](https://rocksdb.org/) key-value store.
/// ///
/// This store ensure the "repeatable read" isolation level. /// This store ensure the "repeatable read" isolation level: the store only exposes changes that have
/// All operations are not able to read the result of concurrent /// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
/// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
/// ///
/// Usage example: /// Usage example:
/// ``` /// ```
@ -721,7 +722,7 @@ impl Store {
impl fmt::Display for Store { impl fmt::Display for Store {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for t in self.iter() { for t in self.iter() {
writeln!(f, "{}", t.map_err(|_| fmt::Error)?)?; writeln!(f, "{} .", t.map_err(|_| fmt::Error)?)?;
} }
Ok(()) Ok(())
} }

@ -16,9 +16,6 @@ Pyoxigraph is a Python graph database library implementing the `SPARQL <https://
It is built on top of `Oxigraph <https://crates.io/crates/oxigraph>`_ using `PyO3 <https://pyo3.rs/>`_. It is built on top of `Oxigraph <https://crates.io/crates/oxigraph>`_ using `PyO3 <https://pyo3.rs/>`_.
It offers two stores with `SPARQL 1.1 <https://www.w3.org/TR/sparql11-overview/>`_ capabilities.
One of the store is in-memory, and the other one is disk based.
It also provides a set of utility functions for reading, writing and processing RDF files in It also provides a set of utility functions for reading, writing and processing RDF files in
`Turtle <https://www.w3.org/TR/turtle/>`_, `Turtle <https://www.w3.org/TR/turtle/>`_,
`TriG <https://www.w3.org/TR/trig/>`_, `TriG <https://www.w3.org/TR/trig/>`_,
@ -28,7 +25,7 @@ It also provides a set of utility functions for reading, writing and processing
Pyoxigraph is `distributed on Pypi <https://pypi.org/project/pyoxigraph/>`_. Pyoxigraph is `distributed on Pypi <https://pypi.org/project/pyoxigraph/>`_.
There exists also a small library providing `rdflib <https://rdflib.readthedocs.io>`_ stores using pyoxigraph: `oxrdflib <https://github.com/oxigraph/oxrdflib>`_. There is also a small library providing an `rdflib <https://rdflib.readthedocs.io>`_ store using pyoxigraph: `oxrdflib <https://github.com/oxigraph/oxrdflib>`_.
Oxigraph and pyoxigraph source code are on `GitHub <https://github.com/oxigraph/oxigraph/tree/master/python>`_. Oxigraph and pyoxigraph source code are on `GitHub <https://github.com/oxigraph/oxigraph/tree/master/python>`_.

@ -16,7 +16,7 @@ pub fn add_to_module(module: &PyModule) -> PyResult<()> {
module.add_wrapped(wrap_pyfunction!(serialize)) module.add_wrapped(wrap_pyfunction!(serialize))
} }
/// Parses RDF graph and dataset serialization formats /// Parses RDF graph and dataset serialization formats.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// ///
@ -32,14 +32,14 @@ pub fn add_to_module(module: &PyModule) -> PyResult<()> {
/// ///
/// :param input: The binary I/O object to read from. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'rb')``. /// :param input: The binary I/O object to read from. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'rb')``.
/// :type input: io.RawIOBase or io.BufferedIOBase /// :type input: io.RawIOBase or io.BufferedIOBase
/// :param mime_type: the MIME type of the RDF serialization /// :param mime_type: the MIME type of the RDF serialization.
/// :type mime_type: str /// :type mime_type: str
/// :param base_iri: the base IRI used to resolve the relative IRIs in the file or :py:const:`None` if relative IRI resolution should not be done /// :param base_iri: the base IRI used to resolve the relative IRIs in the file or :py:const:`None` if relative IRI resolution should not be done.
/// :type base_iri: str or None, optional /// :type base_iri: str or None, optional
/// :return: an iterator of RDF triples or quads depending on the format /// :return: an iterator of RDF triples or quads depending on the format.
/// :rtype: iter(Triple) or iter(Quad) /// :rtype: iter(Triple) or iter(Quad)
/// :raises ValueError: if the MIME type is not supported /// :raises ValueError: if the MIME type is not supported.
/// :raises SyntaxError: if the provided data is invalid /// :raises SyntaxError: if the provided data is invalid.
/// ///
/// >>> input = io.BytesIO(b'<foo> <p> "1" .') /// >>> input = io.BytesIO(b'<foo> <p> "1" .')
/// >>> list(parse(input, "text/turtle", base_iri="http://example.com/")) /// >>> list(parse(input, "text/turtle", base_iri="http://example.com/"))
@ -83,7 +83,7 @@ pub fn parse(
} }
} }
/// Serializes an RDF graph or dataset /// Serializes an RDF graph or dataset.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// ///
@ -97,14 +97,14 @@ pub fn parse(
/// For example ``application/turtle`` could also be used for `Turtle <https://www.w3.org/TR/turtle/>`_ /// For example ``application/turtle`` could also be used for `Turtle <https://www.w3.org/TR/turtle/>`_
/// and ``application/xml`` for `RDF/XML <https://www.w3.org/TR/rdf-syntax-grammar/>`_. /// and ``application/xml`` for `RDF/XML <https://www.w3.org/TR/rdf-syntax-grammar/>`_.
/// ///
/// :param input: the RDF triples and quads to serialize /// :param input: the RDF triples and quads to serialize.
/// :type input: iter(Triple) or iter(Quad) /// :type input: iter(Triple) or iter(Quad)
/// :param output: The binary I/O object to write to. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'wb')``. /// :param output: The binary I/O object to write to. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'wb')``.
/// :type output: io.RawIOBase or io.BufferedIOBase /// :type output: io.RawIOBase or io.BufferedIOBase
/// :param mime_type: the MIME type of the RDF serialization /// :param mime_type: the MIME type of the RDF serialization.
/// :type mime_type: str /// :type mime_type: str
/// :raises ValueError: if the MIME type is not supported /// :raises ValueError: if the MIME type is not supported.
/// :raises TypeError: if a triple is given during a quad format serialization or reverse /// :raises TypeError: if a triple is given during a quad format serialization or reverse.
/// ///
/// >>> output = io.BytesIO() /// >>> output = io.BytesIO()
/// >>> serialize([Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'))], output, "text/turtle") /// >>> serialize([Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'))], output, "text/turtle")

@ -9,11 +9,11 @@ use std::hash::Hash;
use std::hash::Hasher; use std::hash::Hasher;
use std::vec::IntoIter; use std::vec::IntoIter;
/// An RDF `node identified by an IRI <https://www.w3.org/TR/rdf11-concepts/#dfn-iri>`_ /// An RDF `node identified by an IRI <https://www.w3.org/TR/rdf11-concepts/#dfn-iri>`_.
/// ///
/// :param value: the IRI as a string /// :param value: the IRI as a string.
/// :type value: str /// :type value: str
/// :raises ValueError: if the IRI is not valid according to `RFC 3987 <https://tools.ietf.org/rfc/rfc3987>`_ /// :raises ValueError: if the IRI is not valid according to `RFC 3987 <https://tools.ietf.org/rfc/rfc3987>`_.
/// ///
/// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL: /// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL:
/// ///
@ -71,7 +71,7 @@ impl PyNamedNode {
.into()) .into())
} }
/// :return: the named node IRI /// :return: the named node IRI.
/// :rtype: str /// :rtype: str
/// ///
/// >>> NamedNode("http://example.com").value /// >>> NamedNode("http://example.com").value
@ -111,7 +111,7 @@ impl PyNamedNode {
} }
} }
/// An RDF `blank node <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node>`_ /// An RDF `blank node <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node>`_.
/// ///
/// :param value: the `blank node ID <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier>`_ (if not present, a random blank node ID is automatically generated). /// :param value: the `blank node ID <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier>`_ (if not present, a random blank node ID is automatically generated).
/// :type value: str, optional /// :type value: str, optional
@ -176,7 +176,7 @@ impl PyBlankNode {
.into()) .into())
} }
/// :return: the `blank node ID <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier>`_ /// :return: the `blank node ID <https://www.w3.org/TR/rdf11-concepts/#dfn-blank-node-identifier>`_.
/// :rtype: str /// :rtype: str
/// ///
/// >>> BlankNode("ex").value /// >>> BlankNode("ex").value
@ -216,15 +216,15 @@ impl PyBlankNode {
} }
} }
/// An RDF `literal <https://www.w3.org/TR/rdf11-concepts/#dfn-literal>`_ /// An RDF `literal <https://www.w3.org/TR/rdf11-concepts/#dfn-literal>`_.
/// ///
/// :param value: the literal value or `lexical form <https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form>`_ /// :param value: the literal value or `lexical form <https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form>`_.
/// :type value: str /// :type value: str
/// :param datatype: the literal `datatype IRI <https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri>`_. /// :param datatype: the literal `datatype IRI <https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri>`_.
/// :type datatype: NamedNode, optional /// :type datatype: NamedNode, optional
/// :param language: the literal `language tag <https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag>`_ /// :param language: the literal `language tag <https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag>`_.
/// :type language: str, optional /// :type language: str, optional
/// :raises ValueError: if the language tag is not valid according to `RFC 5646 <https://tools.ietf.org/rfc/rfc5646>`_ (`BCP 47 <https://tools.ietf.org/rfc/bcp/bcp47>`_) /// :raises ValueError: if the language tag is not valid according to `RFC 5646 <https://tools.ietf.org/rfc/rfc5646>`_ (`BCP 47 <https://tools.ietf.org/rfc/bcp/bcp47>`_).
/// ///
/// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL: /// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL:
/// ///
@ -286,7 +286,7 @@ impl PyLiteral {
.into()) .into())
} }
/// :return: the literal value or `lexical form <https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form>`_ /// :return: the literal value or `lexical form <https://www.w3.org/TR/rdf11-concepts/#dfn-lexical-form>`_.
/// :rtype: str /// :rtype: str
/// ///
/// >>> Literal("example").value /// >>> Literal("example").value
@ -296,7 +296,7 @@ impl PyLiteral {
self.inner.value() self.inner.value()
} }
/// :return: the literal `language tag <https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag>`_ /// :return: the literal `language tag <https://www.w3.org/TR/rdf11-concepts/#dfn-language-tag>`_.
/// :rtype: str or None /// :rtype: str or None
/// ///
/// >>> Literal('example', language='en').language /// >>> Literal('example', language='en').language
@ -308,7 +308,7 @@ impl PyLiteral {
self.inner.language() self.inner.language()
} }
/// :return: the literal `datatype IRI <https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri>`_ /// :return: the literal `datatype IRI <https://www.w3.org/TR/rdf11-concepts/#dfn-datatype-iri>`_.
/// :rtype: NamedNode /// :rtype: NamedNode
/// ///
/// >>> Literal('11', datatype=NamedNode('http://www.w3.org/2001/XMLSchema#integer')).datatype /// >>> Literal('11', datatype=NamedNode('http://www.w3.org/2001/XMLSchema#integer')).datatype
@ -352,7 +352,7 @@ impl PyLiteral {
} }
} }
/// The RDF `default graph name <https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph>`_ /// The RDF `default graph name <https://www.w3.org/TR/rdf11-concepts/#dfn-default-graph>`_.
#[pyclass(name = "DefaultGraph", module = "oxigraph")] #[pyclass(name = "DefaultGraph", module = "oxigraph")]
#[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)] #[derive(Eq, PartialEq, Debug, Clone, Copy, Hash)]
pub struct PyDefaultGraph {} pub struct PyDefaultGraph {}
@ -514,19 +514,19 @@ impl IntoPy<PyObject> for PyTerm {
} }
} }
/// An RDF `triple <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple>`_ /// An RDF `triple <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple>`_.
/// ///
/// :param subject: the triple subject /// :param subject: the triple subject.
/// :type subject: NamedNode or BlankNode /// :type subject: NamedNode or BlankNode
/// :param predicate: the triple predicate /// :param predicate: the triple predicate.
/// :type predicate: NamedNode /// :type predicate: NamedNode
/// :param object: the triple object /// :param object: the triple object.
/// :type object: NamedNode or BlankNode or Literal /// :type object: NamedNode or BlankNode or Literal
/// ///
/// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL: /// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL:
/// ///
/// >>> str(Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'))) /// >>> str(Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')))
/// '<http://example.com> <http://example.com/p> "1" .' /// '<http://example.com> <http://example.com/p> "1"'
/// ///
/// A triple could also be easily destructed into its components: /// A triple could also be easily destructed into its components:
/// ///
@ -575,7 +575,7 @@ impl PyTriple {
Triple::new(subject, predicate, object).into() Triple::new(subject, predicate, object).into()
} }
/// :return: the triple subject /// :return: the triple subject.
/// :rtype: NamedNode or BlankNode /// :rtype: NamedNode or BlankNode
/// ///
/// >>> Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')).subject /// >>> Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')).subject
@ -585,7 +585,7 @@ impl PyTriple {
self.inner.subject.clone().into() self.inner.subject.clone().into()
} }
/// :return: the triple predicate /// :return: the triple predicate.
/// :rtype: NamedNode /// :rtype: NamedNode
/// ///
/// >>> Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')).predicate /// >>> Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')).predicate
@ -595,7 +595,7 @@ impl PyTriple {
self.inner.predicate.clone().into() self.inner.predicate.clone().into()
} }
/// :return: the triple object /// :return: the triple object.
/// :rtype: NamedNode or BlankNode or Literal /// :rtype: NamedNode or BlankNode or Literal
/// ///
/// >>> Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')).object /// >>> Triple(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1')).object
@ -685,14 +685,14 @@ impl IntoPy<PyObject> for PyGraphName {
} }
} }
/// An RDF `triple <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple>`_ /// An RDF `triple <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-triple>`_.
/// in a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ /// in a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_.
/// ///
/// :param subject: the quad subject /// :param subject: the quad subject.
/// :type subject: NamedNode or BlankNode /// :type subject: NamedNode or BlankNode
/// :param predicate: the quad predicate /// :param predicate: the quad predicate.
/// :type predicate: NamedNode /// :type predicate: NamedNode
/// :param object: the quad object /// :param object: the quad object.
/// :type object: NamedNode or BlankNode or Literal /// :type object: NamedNode or BlankNode or Literal
/// :param graph: the quad graph name. If not present, the default graph is assumed. /// :param graph: the quad graph name. If not present, the default graph is assumed.
/// :type graph: NamedNode or BlankNode or DefaultGraph or None, optional /// :type graph: NamedNode or BlankNode or DefaultGraph or None, optional
@ -700,10 +700,10 @@ impl IntoPy<PyObject> for PyGraphName {
/// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL: /// The :py:func:`str` function provides a serialization compatible with NTriples, Turtle and SPARQL:
/// ///
/// >>> str(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))) /// >>> str(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')))
/// '<http://example.com> <http://example.com/p> "1" <http://example.com/g> .' /// '<http://example.com> <http://example.com/p> "1" <http://example.com/g>'
/// ///
/// >>> str(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), DefaultGraph())) /// >>> str(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), DefaultGraph()))
/// '<http://example.com> <http://example.com/p> "1" .' /// '<http://example.com> <http://example.com/p> "1"'
/// ///
/// A quad could also be easily destructed into its components: /// A quad could also be easily destructed into its components:
/// ///
@ -751,7 +751,7 @@ impl PyQuad {
.into() .into()
} }
/// :return: the quad subject /// :return: the quad subject.
/// :rtype: NamedNode or BlankNode /// :rtype: NamedNode or BlankNode
/// ///
/// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).subject /// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).subject
@ -761,7 +761,7 @@ impl PyQuad {
self.inner.subject.clone().into() self.inner.subject.clone().into()
} }
/// :return: the quad predicate /// :return: the quad predicate.
/// :rtype: NamedNode /// :rtype: NamedNode
/// ///
/// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).predicate /// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).predicate
@ -771,7 +771,7 @@ impl PyQuad {
self.inner.predicate.clone().into() self.inner.predicate.clone().into()
} }
/// :return: the quad object /// :return: the quad object.
/// :rtype: NamedNode or BlankNode or Literal /// :rtype: NamedNode or BlankNode or Literal
/// ///
/// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).object /// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).object
@ -781,7 +781,7 @@ impl PyQuad {
self.inner.object.clone().into() self.inner.object.clone().into()
} }
/// :return: the quad graph name /// :return: the quad graph name.
/// :rtype: NamedNode or BlankNode or DefaultGraph /// :rtype: NamedNode or BlankNode or DefaultGraph
/// ///
/// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).graph_name /// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).graph_name
@ -791,7 +791,7 @@ impl PyQuad {
self.inner.graph_name.clone().into() self.inner.graph_name.clone().into()
} }
/// :return: the quad underlying triple /// :return: the quad underlying triple.
/// :rtype: Triple /// :rtype: Triple
/// ///
/// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).triple /// >>> Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')).triple
@ -860,9 +860,9 @@ impl PyQuad {
} }
} }
/// A SPARQL query variable /// A SPARQL query variable.
/// ///
/// :param value: the variable name as a string /// :param value: the variable name as a string.
/// :type value: str /// :type value: str
/// :raises ValueError: if the variable name is invalid according to the SPARQL grammar. /// :raises ValueError: if the variable name is invalid according to the SPARQL grammar.
/// ///
@ -904,7 +904,7 @@ impl PyVariable {
.into()) .into())
} }
/// :return: the variable name /// :return: the variable name.
/// :rtype: str /// :rtype: str
/// ///
/// >>> Variable("foo").value /// >>> Variable("foo").value

@ -9,11 +9,12 @@ use std::vec::IntoIter;
pub fn parse_query( pub fn parse_query(
query: &str, query: &str,
base_iri: Option<&str>,
use_default_graph_as_union: bool, use_default_graph_as_union: bool,
default_graph: Option<&PyAny>, default_graph: Option<&PyAny>,
named_graphs: Option<&PyAny>, named_graphs: Option<&PyAny>,
) -> PyResult<Query> { ) -> PyResult<Query> {
let mut query = Query::parse(query, None).map_err(|e| map_evaluation_error(e.into()))?; let mut query = Query::parse(query, base_iri).map_err(|e| map_evaluation_error(e.into()))?;
if use_default_graph_as_union && default_graph.is_some() { if use_default_graph_as_union && default_graph.is_some() {
return Err(PyValueError::new_err( return Err(PyValueError::new_err(

@ -5,6 +5,7 @@ use crate::model::*;
use crate::sparql::*; use crate::sparql::*;
use oxigraph::io::{DatasetFormat, GraphFormat}; use oxigraph::io::{DatasetFormat, GraphFormat};
use oxigraph::model::GraphNameRef; use oxigraph::model::GraphNameRef;
use oxigraph::sparql::Update;
use oxigraph::store::{self, Store}; use oxigraph::store::{self, Store};
use pyo3::exceptions::PyValueError; use pyo3::exceptions::PyValueError;
use pyo3::prelude::*; use pyo3::prelude::*;
@ -14,11 +15,15 @@ use std::io::BufReader;
/// Disk-based RDF store. /// Disk-based RDF store.
/// ///
/// It encodes a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ and allows to query it using SPARQL. /// It encodes a `RDF dataset <https://www.w3.org/TR/rdf11-concepts/#dfn-rdf-dataset>`_ and allows to query it using SPARQL.
/// It is based on the `RocksDB <https://rocksdb.org/>`_ key-value database /// It is based on the `RocksDB <https://rocksdb.org/>`_ key-value database.
///
/// This store ensure the "repeatable read" isolation level: the store only exposes changes that have
/// been "committed" (i.e. no partial writes) and the exposed state does not change for the complete duration
/// of a read operation (e.g. a SPARQL query) or a read/write operation (e.g. a SPARQL update).
/// ///
/// :param path: the path of the directory in which the store should read and write its data. If the directory does not exist, it is created. If no directory is provided a temporary one is created and removed when the Python garbage collector removes the store. /// :param path: the path of the directory in which the store should read and write its data. If the directory does not exist, it is created. If no directory is provided a temporary one is created and removed when the Python garbage collector removes the store.
/// :type path: str or None, optional /// :type path: str or None, optional.
/// :raises IOError: if the target directory contains invalid data or could not be accessed /// :raises IOError: if the target directory contains invalid data or could not be accessed.
/// ///
/// The :py:func:`str` function provides a serialization of the store in NQuads: /// The :py:func:`str` function provides a serialization of the store in NQuads:
/// ///
@ -47,11 +52,11 @@ impl PyStore {
}) })
} }
/// Adds a quad to the store /// Adds a quad to the store.
/// ///
/// :param quad: the quad to add /// :param quad: the quad to add.
/// :type quad: Quad /// :type quad: Quad
/// :raises IOError: if an I/O error happens during the quad insertion /// :raises IOError: if an I/O error happens during the quad insertion.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))) /// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')))
@ -63,11 +68,11 @@ impl PyStore {
Ok(()) Ok(())
} }
/// Removes a quad from the store /// Removes a quad from the store.
/// ///
/// :param quad: the quad to remove /// :param quad: the quad to remove.
/// :type quad: Quad /// :type quad: Quad
/// :raises IOError: if an I/O error happens during the quad removal /// :raises IOError: if an I/O error happens during the quad removal.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> quad = Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')) /// >>> quad = Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))
@ -81,7 +86,7 @@ impl PyStore {
Ok(()) Ok(())
} }
/// Looks for the quads matching a given pattern /// Looks for the quads matching a given pattern.
/// ///
/// :param subject: the quad subject or :py:const:`None` to match everything. /// :param subject: the quad subject or :py:const:`None` to match everything.
/// :type subject: NamedNode or BlankNode or None /// :type subject: NamedNode or BlankNode or None
@ -91,9 +96,9 @@ impl PyStore {
/// :type object: NamedNode or BlankNode or Literal or None /// :type object: NamedNode or BlankNode or Literal or None
/// :param graph: the quad graph name. To match only the default graph, use :py:class:`DefaultGraph`. To match everything use :py:const:`None`. /// :param graph: the quad graph name. To match only the default graph, use :py:class:`DefaultGraph`. To match everything use :py:const:`None`.
/// :type graph: NamedNode or BlankNode or DefaultGraph or None /// :type graph: NamedNode or BlankNode or DefaultGraph or None
/// :return: an iterator of the quads matching the pattern /// :return: an iterator of the quads matching the pattern.
/// :rtype: iter(Quad) /// :rtype: iter(Quad)
/// :raises IOError: if an I/O error happens during the quads lookup /// :raises IOError: if an I/O error happens during the quads lookup.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))) /// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')))
@ -121,8 +126,10 @@ impl PyStore {
/// Executes a `SPARQL 1.1 query <https://www.w3.org/TR/sparql11-query/>`_. /// Executes a `SPARQL 1.1 query <https://www.w3.org/TR/sparql11-query/>`_.
/// ///
/// :param query: the query to execute /// :param query: the query to execute.
/// :type query: str /// :type query: str
/// :param base_iri: the base IRI used to resolve the relative IRIs in the SPARQL query or :py:const:`None` if relative IRI resolution should not be done.
/// :type base_iri: str or None, optional
/// :param use_default_graph_as_union: if the SPARQL query should look for triples in all the dataset graphs by default (i.e. without `GRAPH` operations). Disabled by default. /// :param use_default_graph_as_union: if the SPARQL query should look for triples in all the dataset graphs by default (i.e. without `GRAPH` operations). Disabled by default.
/// :type use_default_graph_as_union: bool, optional /// :type use_default_graph_as_union: bool, optional
/// :param default_graph: list of the graphs that should be used as the query default graph. By default, the store default graph is used. /// :param default_graph: list of the graphs that should be used as the query default graph. By default, the store default graph is used.
@ -131,8 +138,8 @@ impl PyStore {
/// :type named_graphs: list(NamedNode or BlankNode) or None, optional /// :type named_graphs: list(NamedNode or BlankNode) or None, optional
/// :return: a :py:class:`bool` for ``ASK`` queries, an iterator of :py:class:`Triple` for ``CONSTRUCT`` and ``DESCRIBE`` queries and an iterator of :py:class:`QuerySolution` for ``SELECT`` queries. /// :return: a :py:class:`bool` for ``ASK`` queries, an iterator of :py:class:`Triple` for ``CONSTRUCT`` and ``DESCRIBE`` queries and an iterator of :py:class:`QuerySolution` for ``SELECT`` queries.
/// :rtype: QuerySolutions or QueryTriples or bool /// :rtype: QuerySolutions or QueryTriples or bool
/// :raises SyntaxError: if the provided query is invalid /// :raises SyntaxError: if the provided query is invalid.
/// :raises IOError: if an I/O error happens while reading the store /// :raises IOError: if an I/O error happens while reading the store.
/// ///
/// ``SELECT`` query: /// ``SELECT`` query:
/// ///
@ -155,11 +162,12 @@ impl PyStore {
/// >>> store.query('ASK { ?s ?p ?o }') /// >>> store.query('ASK { ?s ?p ?o }')
/// True /// True
#[pyo3( #[pyo3(
text_signature = "($self, query, *, use_default_graph_as_union, default_graph, named_graphs)" text_signature = "($self, query, *, base_iri, use_default_graph_as_union, default_graph, named_graphs)"
)] )]
#[args( #[args(
query, query,
"*", "*",
base_iri = "None",
use_default_graph_as_union = "false", use_default_graph_as_union = "false",
default_graph = "None", default_graph = "None",
named_graphs = "None" named_graphs = "None"
@ -167,6 +175,7 @@ impl PyStore {
fn query( fn query(
&self, &self,
query: &str, query: &str,
base_iri: Option<&str>,
use_default_graph_as_union: bool, use_default_graph_as_union: bool,
default_graph: Option<&PyAny>, default_graph: Option<&PyAny>,
named_graphs: Option<&PyAny>, named_graphs: Option<&PyAny>,
@ -174,6 +183,7 @@ impl PyStore {
) -> PyResult<PyObject> { ) -> PyResult<PyObject> {
let query = parse_query( let query = parse_query(
query, query,
base_iri,
use_default_graph_as_union, use_default_graph_as_union,
default_graph, default_graph,
named_graphs, named_graphs,
@ -184,10 +194,14 @@ impl PyStore {
/// Executes a `SPARQL 1.1 update <https://www.w3.org/TR/sparql11-update/>`_. /// Executes a `SPARQL 1.1 update <https://www.w3.org/TR/sparql11-update/>`_.
/// ///
/// :param update: the update to execute /// Updates are applied in a transactional manner: either the full operation succeeds or nothing is written to the database.
///
/// :param update: the update to execute.
/// :type update: str /// :type update: str
/// :raises SyntaxError: if the provided update is invalid /// :param base_iri: the base IRI used to resolve the relative IRIs in the SPARQL update or :py:const:`None` if relative IRI resolution should not be done.
/// :raises IOError: if an I/O error happens while reading the store /// :type base_iri: str or None, optional
/// :raises SyntaxError: if the provided update is invalid.
/// :raises IOError: if an I/O error happens while reading the store.
/// ///
/// The store does not track the existence of empty named graphs. /// The store does not track the existence of empty named graphs.
/// This method has no ACID guarantees. /// This method has no ACID guarantees.
@ -214,12 +228,19 @@ impl PyStore {
/// >>> store.update('DELETE WHERE { <http://example.com> ?p ?o }') /// >>> store.update('DELETE WHERE { <http://example.com> ?p ?o }')
/// >>> list(store) /// >>> list(store)
/// [] /// []
#[pyo3(text_signature = "($self, update)")] #[pyo3(text_signature = "($self, update, *, base_iri)")]
fn update(&self, update: &str) -> PyResult<()> { #[args(update, "*", base_iri = "None")]
fn update(&self, update: &str, base_iri: Option<&str>) -> PyResult<()> {
let update = Update::parse(update, base_iri).map_err(|e| map_evaluation_error(e.into()))?;
self.inner.update(update).map_err(map_evaluation_error) self.inner.update(update).map_err(map_evaluation_error)
} }
/// Loads an RDF serialization into the store /// Loads an RDF serialization into the store.
///
/// Loads are applied in a transactional manner: either the full operation succeeds or nothing is written to the database.
/// The :py:func:`bulk_load` method is also available for much faster loading of big files but without transactional guarantees.
///
/// Beware, the full file is loaded into memory.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// ///
@ -235,15 +256,15 @@ impl PyStore {
/// ///
/// :param input: The binary I/O object to read from. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'rb')``. /// :param input: The binary I/O object to read from. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'rb')``.
/// :type input: io.RawIOBase or io.BufferedIOBase /// :type input: io.RawIOBase or io.BufferedIOBase
/// :param mime_type: the MIME type of the RDF serialization /// :param mime_type: the MIME type of the RDF serialization.
/// :type mime_type: str /// :type mime_type: str
/// :param base_iri: the base IRI used to resolve the relative IRIs in the file or :py:const:`None` if relative IRI resolution should not be done /// :param base_iri: the base IRI used to resolve the relative IRIs in the file or :py:const:`None` if relative IRI resolution should not be done.
/// :type base_iri: str or None, optional /// :type base_iri: str or None, optional
/// :param to_graph: if it is a file composed of triples, the graph in which store the triples. By default, the default graph is used. /// :param to_graph: if it is a file composed of triples, the graph in which store the triples. By default, the default graph is used.
/// :type to_graph: NamedNode or BlankNode or DefaultGraph or None, optional /// :type to_graph: NamedNode or BlankNode or DefaultGraph or None, optional
/// :raises ValueError: if the MIME type is not supported or the `to_graph` parameter is given with a quad file. /// :raises ValueError: if the MIME type is not supported or the `to_graph` parameter is given with a quad file.
/// :raises SyntaxError: if the provided data is invalid /// :raises SyntaxError: if the provided data is invalid.
/// :raises IOError: if an I/O error happens during a quad insertion /// :raises IOError: if an I/O error happens during a quad insertion.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> store.load(io.BytesIO(b'<foo> <p> "1" .'), "text/turtle", base_iri="http://example.com/", to_graph=NamedNode("http://example.com/g")) /// >>> store.load(io.BytesIO(b'<foo> <p> "1" .'), "text/turtle", base_iri="http://example.com/", to_graph=NamedNode("http://example.com/g"))
@ -290,7 +311,83 @@ impl PyStore {
} }
} }
/// Dumps the store quads or triples into a file /// Loads an RDF serialization into the store.
///
/// This function is designed to be as fast as possible on big files without transactional guarantees.
/// If the file is invalid only a piece of it might be written to the store.
///
/// The :py:func:`load` method is also available for loads with transactional guarantees.
///
/// It currently supports the following formats:
///
/// * `N-Triples <https://www.w3.org/TR/n-triples/>`_ (``application/n-triples``)
/// * `N-Quads <https://www.w3.org/TR/n-quads/>`_ (``application/n-quads``)
/// * `Turtle <https://www.w3.org/TR/turtle/>`_ (``text/turtle``)
/// * `TriG <https://www.w3.org/TR/trig/>`_ (``application/trig``)
/// * `RDF/XML <https://www.w3.org/TR/rdf-syntax-grammar/>`_ (``application/rdf+xml``)
///
/// It supports also some MIME type aliases.
/// For example ``application/turtle`` could also be used for `Turtle <https://www.w3.org/TR/turtle/>`_
/// and ``application/xml`` for `RDF/XML <https://www.w3.org/TR/rdf-syntax-grammar/>`_.
///
/// :param input: The binary I/O object to read from. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'rb')``.
/// :type input: io.RawIOBase or io.BufferedIOBase
/// :param mime_type: the MIME type of the RDF serialization.
/// :type mime_type: str
/// :param base_iri: the base IRI used to resolve the relative IRIs in the file or :py:const:`None` if relative IRI resolution should not be done.
/// :type base_iri: str or None, optional
/// :param to_graph: if it is a file composed of triples, the graph in which store the triples. By default, the default graph is used.
/// :type to_graph: NamedNode or BlankNode or DefaultGraph or None, optional
/// :raises ValueError: if the MIME type is not supported or the `to_graph` parameter is given with a quad file.
/// :raises SyntaxError: if the provided data is invalid.
/// :raises IOError: if an I/O error happens during a quad insertion.
///
/// >>> store = Store()
/// >>> store.bulk_load(io.BytesIO(b'<foo> <p> "1" .'), "text/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>>]
#[pyo3(text_signature = "($self, data, /, mime_type, *, base_iri = None, to_graph = None)")]
#[args(input, mime_type, "*", base_iri = "None", to_graph = "None")]
fn bulk_load(
&mut self,
input: PyObject,
mime_type: &str,
base_iri: Option<&str>,
to_graph: Option<&PyAny>,
) -> PyResult<()> {
let to_graph_name = if let Some(graph_name) = to_graph {
Some(PyGraphNameRef::try_from(graph_name)?)
} else {
None
};
let input = BufReader::new(PyFileLike::new(input));
if let Some(graph_format) = GraphFormat::from_media_type(mime_type) {
self.inner
.bulk_load_graph(
input,
graph_format,
&to_graph_name.unwrap_or(PyGraphNameRef::DefaultGraph),
base_iri,
)
.map_err(map_io_err)
} else if let Some(dataset_format) = DatasetFormat::from_media_type(mime_type) {
if to_graph_name.is_some() {
return Err(PyValueError::new_err(
"The target graph name parameter is not available for dataset formats",
));
}
self.inner
.bulk_load_dataset(input, dataset_format, base_iri)
.map_err(map_io_err)
} else {
Err(PyValueError::new_err(format!(
"Not supported MIME type: {}",
mime_type
)))
}
}
/// Dumps the store quads or triples into a file.
/// ///
/// It currently supports the following formats: /// It currently supports the following formats:
/// ///
@ -306,7 +403,7 @@ impl PyStore {
/// ///
/// :param output: The binary I/O object to write to. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'wb')``. /// :param output: The binary I/O object to write to. For example, it could be a file opened in binary mode with ``open('my_file.ttl', 'wb')``.
/// :type input: io.RawIOBase or io.BufferedIOBase /// :type input: io.RawIOBase or io.BufferedIOBase
/// :param mime_type: the MIME type of the RDF serialization /// :param mime_type: the MIME type of the RDF serialization.
/// :type mime_type: str /// :type mime_type: str
/// :param from_graph: if a triple based format is requested, the store graph from which dump the triples. By default, the default graph is used. /// :param from_graph: if a triple based format is requested, the store graph from which dump the triples. By default, the default graph is used.
/// :type from_graph: NamedNode or BlankNode or DefaultGraph or None, optional /// :type from_graph: NamedNode or BlankNode or DefaultGraph or None, optional
@ -353,11 +450,11 @@ impl PyStore {
} }
} }
/// Returns an iterator over all the store named graphs /// Returns an iterator over all the store named graphs.
/// ///
/// :return: an iterator of the store graph names /// :return: an iterator of the store graph names.
/// :rtype: iter(NamedNode or BlankNode) /// :rtype: iter(NamedNode or BlankNode)
/// :raises IOError: if an I/O error happens during the named graphs lookup /// :raises IOError: if an I/O error happens during the named graphs lookup.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))) /// >>> store.add(Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')))
@ -370,11 +467,11 @@ impl PyStore {
} }
} }
/// Adds a named graph to the store /// Adds a named graph to the store.
/// ///
/// :param graph_name: the name of the name graph to add /// :param graph_name: the name of the name graph to add.
/// :type graph_name: NamedNode or BlankNode /// :type graph_name: NamedNode or BlankNode
/// :raises IOError: if an I/O error happens during the named graph insertion /// :raises IOError: if an I/O error happens during the named graph insertion.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> store.add_graph(NamedNode('http://example.com/g')) /// >>> store.add_graph(NamedNode('http://example.com/g'))
@ -396,13 +493,13 @@ impl PyStore {
.map_err(map_io_err) .map_err(map_io_err)
} }
/// Removes a graph from the store /// Removes a graph from the store.
/// ///
/// The default graph will not be remove but just cleared. /// The default graph will not be remove but just cleared.
/// ///
/// :param graph_name: the name of the name graph to remove /// :param graph_name: the name of the name graph to remove.
/// :type graph_name: NamedNode or BlankNode or DefaultGraph /// :type graph_name: NamedNode or BlankNode or DefaultGraph
/// :raises IOError: if an I/O error happens during the named graph removal /// :raises IOError: if an I/O error happens during the named graph removal.
/// ///
/// >>> store = Store() /// >>> store = Store()
/// >>> quad = Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g')) /// >>> quad = Quad(NamedNode('http://example.com'), NamedNode('http://example.com/p'), Literal('1'), NamedNode('http://example.com/g'))

Loading…
Cancel
Save