diff --git a/python/examples/query.py b/python/examples/query.py
new file mode 100644
index 00000000..ac7baff0
--- /dev/null
+++ b/python/examples/query.py
@@ -0,0 +1,21 @@
+from pyrudof import Rudof, RudofConfig, QuerySolutions
+
+rudof = Rudof(RudofConfig())
+rdf = """prefix :
+:alice a :Person ;
+ :name "Alice" ;
+ :knows :bob .
+:bob a :Person ;
+ :name "Robert" .
+"""
+rudof.read_data_str(rdf)
+
+query = """prefix :
+select * where {
+ ?x a :Person
+}
+"""
+
+results = rudof.run_query_str(query)
+for result in iter(results):
+ print(result.show())
diff --git a/python/src/lib.rs b/python/src/lib.rs
index 5ad8f7f5..77e70757 100644
--- a/python/src/lib.rs
+++ b/python/src/lib.rs
@@ -10,10 +10,10 @@ pub mod pyrudof {
#[pymodule_export]
use super::{
- PyDCTAP, PyDCTapFormat, PyRDFFormat, PyReaderMode, PyRudof, PyRudofConfig, PyRudofError,
- PyShExFormat, PyShExFormatter, PyShaclFormat, PyShaclValidationMode, PyShapeMapFormat,
- PyShapeMapFormatter, PyShapesGraphSource, PyUmlGenerationMode, PyValidationReport,
- PyValidationStatus,
+ PyDCTAP, PyDCTapFormat, PyQuerySolution, PyQuerySolutions, PyRDFFormat, PyReaderMode,
+ PyRudof, PyRudofConfig, PyRudofError, PyShExFormat, PyShExFormatter, PyShaclFormat,
+ PyShaclValidationMode, PyShapeMapFormat, PyShapeMapFormatter, PyShapesGraphSource,
+ PyUmlGenerationMode, PyValidationReport, PyValidationStatus,
};
#[pymodule_init]
diff --git a/python/src/pyrudof_lib.rs b/python/src/pyrudof_lib.rs
index ab9a7da5..eb87bfa5 100644
--- a/python/src/pyrudof_lib.rs
+++ b/python/src/pyrudof_lib.rs
@@ -1,10 +1,12 @@
//! This is a wrapper of the methods provided by `rudof_lib`
//!
-use pyo3::{exceptions::PyValueError, pyclass, pymethods, PyErr, PyResult, Python};
+use pyo3::{
+ exceptions::PyValueError, pyclass, pymethods, Py, PyErr, PyRef, PyRefMut, PyResult, Python,
+};
use rudof_lib::{
- iri, DCTAPFormat, PrefixMap, QueryShapeMap, QuerySolutions, RDFFormat, RdfData, ReaderMode,
- ResultShapeMap, Rudof, RudofConfig, RudofError, ShExFormat, ShExFormatter, ShExSchema,
- ShaclFormat, ShaclSchema, ShaclValidationMode, ShapeMapFormat, ShapeMapFormatter,
+ iri, DCTAPFormat, PrefixMap, QueryShapeMap, QuerySolution, QuerySolutions, RDFFormat, RdfData,
+ ReaderMode, ResultShapeMap, Rudof, RudofConfig, RudofError, ShExFormat, ShExFormatter,
+ ShExSchema, ShaclFormat, ShaclSchema, ShaclValidationMode, ShapeMapFormat, ShapeMapFormatter,
ShapesGraphSource, UmlGenerationMode, ValidationReport, ValidationStatus, DCTAP,
};
use std::{ffi::OsStr, fs::File, io::BufReader, path::Path};
@@ -123,6 +125,28 @@ impl PyRudof {
shacl_schema.map(|s| PyShaclSchema { inner: s.clone() })
}
+ /// Run a SPARQL query obtained from a string on the RDF data
+ #[pyo3(signature = (input))]
+ pub fn run_query_str(&mut self, input: &str) -> PyResult {
+ let results = self.inner.run_query_str(input).map_err(cnv_err)?;
+ Ok(PyQuerySolutions { inner: results })
+ }
+
+ /// Run a SPARQL query obtained from a file path on the RDF data
+ #[pyo3(signature = (path_name))]
+ pub fn run_query_path(&mut self, path_name: &str) -> PyResult {
+ let path = Path::new(path_name);
+ let file = File::open::<&OsStr>(path.as_ref())
+ .map_err(|e| RudofError::ReadingDCTAPPath {
+ path: path_name.to_string(),
+ error: format!("{e}"),
+ })
+ .map_err(cnv_err)?;
+ let mut reader = BufReader::new(file);
+ let results = self.inner.run_query(&mut reader).map_err(cnv_err)?;
+ Ok(PyQuerySolutions { inner: results })
+ }
+
/// Reads DCTAP from a String
#[pyo3(signature = (input, format = &PyDCTapFormat::CSV))]
pub fn read_dctap_str(&mut self, input: &str, format: &PyDCTapFormat) -> PyResult<()> {
@@ -683,15 +707,60 @@ pub enum PyShapesGraphSource {
CurrentSchema,
}
-#[pyclass(name = "QuerySulutions")]
+#[pyclass(name = "QuerySolution")]
+pub struct PyQuerySolution {
+ inner: QuerySolution,
+}
+
+#[pymethods]
+impl PyQuerySolution {
+ pub fn show(&self) -> String {
+ self.inner.show().to_string()
+ }
+}
+
+#[pyclass(name = "QuerySolutions")]
pub struct PyQuerySolutions {
inner: QuerySolutions,
}
+#[pymethods]
impl PyQuerySolutions {
pub fn show(&self) -> String {
format!("Solutions: {:?}", self.inner)
}
+
+ pub fn count(&self) -> usize {
+ self.inner.count()
+ }
+
+ fn __iter__(slf: PyRef<'_, Self>) -> PyResult> {
+ let rs: Vec = slf
+ .inner
+ .iter()
+ .map(|qs| PyQuerySolution { inner: qs.clone() })
+ .collect();
+ let iter = QuerySolutionIter {
+ inner: rs.into_iter(),
+ };
+ Py::new(slf.py(), iter)
+ }
+}
+
+#[pyclass]
+struct QuerySolutionIter {
+ inner: std::vec::IntoIter,
+}
+
+#[pymethods]
+impl QuerySolutionIter {
+ fn __iter__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
+ slf
+ }
+
+ fn __next__(mut slf: PyRefMut<'_, Self>) -> Option {
+ slf.inner.next()
+ }
}
#[pyclass(frozen, name = "ResultShapeMap")]