From 382aa2e01ffb93166c31ce0980484bc9af1b37e2 Mon Sep 17 00:00:00 2001 From: Tpt Date: Tue, 8 Aug 2023 21:15:11 +0200 Subject: [PATCH] Python: allows giving pathlib.Path for input --- python/src/io.rs | 17 +++++++++-------- python/src/store.rs | 23 ++++++++++++----------- python/tests/test_store.py | 8 ++++---- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/python/src/io.rs b/python/src/io.rs index 53e53af4..c7e84757 100644 --- a/python/src/io.rs +++ b/python/src/io.rs @@ -13,6 +13,7 @@ use std::cmp::max; use std::error::Error; use std::fs::File; use std::io::{self, BufRead, BufReader, BufWriter, Cursor, Read, Write}; +use std::path::{Path, PathBuf}; pub fn add_to_module(module: &PyModule) -> PyResult<()> { module.add_wrapped(wrap_pyfunction!(parse))?; @@ -34,7 +35,7 @@ pub fn add_to_module(module: &PyModule) -> PyResult<()> { /// and ``application/xml`` for `RDF/XML `_. /// /// :param input: The binary I/O object or file path to read from. For example, it could be a file path as a string or a file reader opened in binary mode with ``open('my_file.ttl', 'rb')``. -/// :type input: io(bytes) or io(str) or str +/// :type input: io(bytes) or io(str) or str or pathlib.Path /// :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. @@ -55,8 +56,8 @@ pub fn parse( base_iri: Option<&str>, py: Python<'_>, ) -> PyResult { - let input = if let Ok(path) = input.extract::<&str>(py) { - PyReadable::from_file(path, py).map_err(map_io_err)? + let input = if let Ok(path) = input.extract::(py) { + PyReadable::from_file(&path, py).map_err(map_io_err)? } else { PyReadable::from_data(input, py) }; @@ -106,7 +107,7 @@ pub fn parse( /// :param input: the RDF triples and quads to serialize. /// :type input: iterable(Triple) or iterable(Quad) /// :param output: The binary I/O object or file path to write to. For example, it could be a file path as a string or a file writer opened in binary mode with ``open('my_file.ttl', 'wb')``. -/// :type output: io(bytes) or str +/// :type output: io(bytes) or str or pathlib.Path /// :param mime_type: the MIME type of the RDF serialization. /// :type mime_type: str /// :rtype: None @@ -119,8 +120,8 @@ pub fn parse( /// b' "1" .\n' #[pyfunction] pub fn serialize(input: &PyAny, output: PyObject, mime_type: &str, py: Python<'_>) -> PyResult<()> { - let output = if let Ok(path) = output.extract::<&str>(py) { - PyWritable::from_file(path, py).map_err(map_io_err)? + let output = if let Ok(path) = output.extract::(py) { + PyWritable::from_file(&path, py).map_err(map_io_err)? } else { PyWritable::from_data(output) }; @@ -202,7 +203,7 @@ pub enum PyReadable { } impl PyReadable { - pub fn from_file(file: &str, py: Python<'_>) -> io::Result { + pub fn from_file(file: &Path, py: Python<'_>) -> io::Result { Ok(Self::File(BufReader::new( py.allow_threads(|| File::open(file))?, ))) @@ -253,7 +254,7 @@ pub enum PyWritable { } impl PyWritable { - pub fn from_file(file: &str, py: Python<'_>) -> io::Result { + pub fn from_file(file: &Path, py: Python<'_>) -> io::Result { Ok(Self::File(BufWriter::new( py.allow_threads(|| File::create(file))?, ))) diff --git a/python/src/store.rs b/python/src/store.rs index 5e3c1df9..957c1b5c 100644 --- a/python/src/store.rs +++ b/python/src/store.rs @@ -9,6 +9,7 @@ use oxigraph::sparql::Update; use oxigraph::store::{self, LoaderError, SerializerError, StorageError, Store}; use pyo3::exceptions::{PyIOError, PyRuntimeError, PyValueError}; use pyo3::prelude::*; +use std::path::PathBuf; /// RDF store. /// @@ -26,7 +27,7 @@ use pyo3::prelude::*; /// :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. /// In this case, the store data are kept in memory and never written on disk. -/// :type path: str or None, optional +/// :type path: str or pathlib.Path or None, optional /// :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: @@ -45,7 +46,7 @@ pub struct PyStore { impl PyStore { #[new] #[pyo3(signature = (path = None))] - fn new(path: Option<&str>, py: Python<'_>) -> PyResult { + fn new(path: Option, py: Python<'_>) -> PyResult { py.allow_threads(|| { Ok(Self { inner: if let Some(path) = path { @@ -357,7 +358,7 @@ impl PyStore { /// and ``application/xml`` for `RDF/XML `_. /// /// :param input: The binary I/O object or file path to read from. For example, it could be a file path as a string or a file reader opened in binary mode with ``open('my_file.ttl', 'rb')``. - /// :type input: io(bytes) or io(str) or str + /// :type input: io(bytes) or io(str) or str or pathlib.Path /// :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. @@ -387,8 +388,8 @@ impl PyStore { } else { None }; - let input = if let Ok(path) = input.extract::<&str>(py) { - PyReadable::from_file(path, py).map_err(map_io_err)? + let input = if let Ok(path) = input.extract::(py) { + PyReadable::from_file(&path, py).map_err(map_io_err)? } else { PyReadable::from_data(input, py) }; @@ -439,7 +440,7 @@ impl PyStore { /// and ``application/xml`` for `RDF/XML `_. /// /// :param input: The binary I/O object or file path to read from. For example, it could be a file path as a string or a file reader opened in binary mode with ``open('my_file.ttl', 'rb')``. - /// :type input: io(bytes) or io(str) or str + /// :type input: io(bytes) or io(str) or str or pathlib.Path /// :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. @@ -469,8 +470,8 @@ impl PyStore { } else { None }; - let input = if let Ok(path) = input.extract::<&str>(py) { - PyReadable::from_file(path, py).map_err(map_io_err)? + let input = if let Ok(path) = input.extract::(py) { + PyReadable::from_file(&path, py).map_err(map_io_err)? } else { PyReadable::from_data(input, py) }; @@ -518,7 +519,7 @@ impl PyStore { /// and ``application/xml`` for `RDF/XML `_. /// /// :param output: The binary I/O object or file path to write to. For example, it could be a file path as a string or a file writer opened in binary mode with ``open('my_file.ttl', 'wb')``. - /// :type output: io(bytes) or str + /// :type output: io(bytes) or str or pathlib.Path /// :param mime_type: the MIME type of the RDF serialization. /// :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. @@ -541,8 +542,8 @@ impl PyStore { from_graph: Option<&PyAny>, py: Python<'_>, ) -> PyResult<()> { - let output = if let Ok(path) = output.extract::<&str>(py) { - PyWritable::from_file(path, py).map_err(map_io_err)? + let output = if let Ok(path) = output.extract::(py) { + PyWritable::from_file(&path, py).map_err(map_io_err)? } else { PyWritable::from_data(output) }; diff --git a/python/tests/test_store.py b/python/tests/test_store.py index 8883b7ef..d6693c4e 100644 --- a/python/tests/test_store.py +++ b/python/tests/test_store.py @@ -266,11 +266,11 @@ class TestStore(unittest.TestCase): def test_load_file(self) -> None: with NamedTemporaryFile(delete=False) as fp: - file_name = fp.name + file_name = Path(fp.name) fp.write(b" .") store = Store() store.load(file_name, mime_type="application/n-quads") - Path(file_name).unlink() + file_name.unlink() self.assertEqual(set(store), {Quad(foo, bar, baz, graph)}) def test_load_with_io_error(self) -> None: @@ -310,12 +310,12 @@ class TestStore(unittest.TestCase): def test_dump_file(self) -> None: with NamedTemporaryFile(delete=False) as fp: - file_name = fp.name + file_name = Path(fp.name) store = Store() store.add(Quad(foo, bar, baz, graph)) store.dump(file_name, "application/n-quads") self.assertEqual( - Path(file_name).read_text(), + file_name.read_text(), " .\n", )