From d46e1e31419b3b47c2ca5e2363b7914f3929eda7 Mon Sep 17 00:00:00 2001 From: Ziyang Hu Date: Tue, 1 Nov 2022 13:13:46 +0800 Subject: [PATCH] python module --- Cargo.lock | 121 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 +- python/.gitignore | 72 +++++++++++++++++++++++++ python/Cargo.toml | 15 ++++++ python/pyproject.toml | 14 +++++ python/src/lib.rs | 55 +++++++++++++++++++ 6 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 python/.gitignore create mode 100644 python/Cargo.toml create mode 100644 python/pyproject.toml create mode 100644 python/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 4b406b28..37ae9003 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -378,6 +378,16 @@ dependencies = [ "uuid", ] +[[package]] +name = "cozo_py_module" +version = "0.1.1" +dependencies = [ + "cozo", + "miette", + "pyo3", + "serde_json", +] + [[package]] name = "cozorocks" version = "0.1.0" @@ -727,6 +737,12 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indoc" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adab1eaa3408fb7f0c777a73e7465fd5656136fc93b670eb6df3c88c2c1344e3" + [[package]] name = "instant" version = "0.1.12" @@ -802,6 +818,16 @@ dependencies = [ "cc", ] +[[package]] +name = "lock_api" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.17" @@ -1053,6 +1079,29 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc9e0dc2adc1c69d09143aff38d3d30c5c3f0df0dad82e6d25547af174ebec0" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "smallvec", + "windows-sys", +] + [[package]] name = "parse-zoneinfo" version = "0.3.0" @@ -1212,6 +1261,66 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "pyo3" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "201b6887e5576bf2f945fe65172c1fcbf3fcf285b23e4d71eb171d9736e38d32" +dependencies = [ + "cfg-if 1.0.0", + "indoc", + "libc", + "memoffset", + "parking_lot", + "pyo3-build-config", + "pyo3-ffi", + "pyo3-macros", + "unindent", +] + +[[package]] +name = "pyo3-build-config" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf0708c9ed01692635cbf056e286008e5a2927ab1a5e48cdd3aeb1ba5a6fef47" +dependencies = [ + "once_cell", + "target-lexicon", +] + +[[package]] +name = "pyo3-ffi" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90352dea4f486932b72ddf776264d293f85b79a1d214de1d023927b41461132d" +dependencies = [ + "libc", + "pyo3-build-config", +] + +[[package]] +name = "pyo3-macros" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb24b804a2d9e88bfcc480a5a6dd76f006c1e3edaf064e8250423336e2cd79d" +dependencies = [ + "proc-macro2", + "pyo3-macros-backend", + "quote", + "syn", +] + +[[package]] +name = "pyo3-macros-backend" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f22bb49f6a7348c253d7ac67a6875f2dc65f36c2ae64a82c381d528972bea6d6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quick-error" version = "1.2.3" @@ -1612,6 +1721,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c02424087780c9b71cc96799eaeddff35af2bc513278cda5c99fc1f5d026d3c1" + [[package]] name = "tempfile" version = "3.3.0" @@ -1854,6 +1969,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "unindent" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58ee9362deb4a96cef4d437d1ad49cffc9b9e92d202b6995674e928ce684f112" + [[package]] name = "untrusted" version = "0.7.1" diff --git a/Cargo.toml b/Cargo.toml index 2642b531..9808aa04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -68,4 +68,4 @@ lto = true #debug = true [workspace] -members = ["cozorocks"] +members = ["cozorocks", "python"] diff --git a/python/.gitignore b/python/.gitignore new file mode 100644 index 00000000..af3ca5ef --- /dev/null +++ b/python/.gitignore @@ -0,0 +1,72 @@ +/target + +# Byte-compiled / optimized / DLL files +__pycache__/ +.pytest_cache/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +.venv/ +env/ +bin/ +build/ +develop-eggs/ +dist/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +include/ +man/ +venv/ +*.egg-info/ +.installed.cfg +*.egg + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt +pip-selfcheck.json + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo + +# Mr Developer +.mr.developer.cfg +.project +.pydevproject + +# Rope +.ropeproject + +# Django stuff: +*.log +*.pot + +.DS_Store + +# Sphinx documentation +docs/_build/ + +# PyCharm +.idea/ + +# VSCode +.vscode/ + +# Pyenv +.python-version \ No newline at end of file diff --git a/python/Cargo.toml b/python/Cargo.toml new file mode 100644 index 00000000..e8200454 --- /dev/null +++ b/python/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "cozo_py_module" +version = "0.1.1" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "cozo_py_module" +crate-type = ["cdylib"] + +[dependencies] +pyo3 = { version = "0.17.1", features = ["extension-module"] } +cozo = { version = "0.1.1", path = ".." } +miette = { version = "=5.3.0", features = ["fancy"] } +serde_json = "1.0.81" \ No newline at end of file diff --git a/python/pyproject.toml b/python/pyproject.toml new file mode 100644 index 00000000..d9339978 --- /dev/null +++ b/python/pyproject.toml @@ -0,0 +1,14 @@ +[build-system] +requires = ["maturin>=0.13,<0.14"] +build-backend = "maturin" + +[project] +name = "python" +requires-python = ">=3.7" +classifiers = [ + "Programming Language :: Rust", + "Programming Language :: Python :: Implementation :: CPython", + "Programming Language :: Python :: Implementation :: PyPy", +] + + diff --git a/python/src/lib.rs b/python/src/lib.rs new file mode 100644 index 00000000..f79a82dd --- /dev/null +++ b/python/src/lib.rs @@ -0,0 +1,55 @@ +/* + * Copyright 2022, The Cozo Project Authors. Licensed under MIT/Apache-2.0/BSD-3-Clause. + */ + +use std::collections::BTreeMap; + +use miette::miette; +use pyo3::exceptions::PyException; +use pyo3::prelude::*; + +use cozo::Db; + +#[pyclass(extends=PyException)] +struct ErrorBridge(cozo::Error); +trait PyResultExt { + fn into_py_res(self) -> PyResult; +} +impl PyResultExt for miette::Result { + fn into_py_res(self) -> PyResult { + match self { + Ok(t) => Ok(t), + Err(e) => Err(PyException::new_err(format!("{:?}", e))), + } + } +} +#[pyclass] +struct CozoDbPy { + db: Db, +} +#[pymethods] +impl CozoDbPy { + #[new] + #[args(create_if_missing = true, destroy_on_exit = false)] + fn new(path: &str) -> PyResult { + let db = Db::new(path).into_py_res()?; + Ok(Self { db }) + } + pub fn run_query(&self, py: Python<'_>, query: &str, params: &str) -> PyResult { + let params_map: serde_json::Value = serde_json::from_str(params) + .map_err(|_| miette!("the given params is not valid JSON")) + .into_py_res()?; + let params_arg: BTreeMap<_, _> = match params_map { + serde_json::Value::Object(m) => m.into_iter().collect(), + _ => Err(miette!("the given params is not a map")).into_py_res()?, + }; + let ret = py.allow_threads(|| self.db.run_script(query, ¶ms_arg).into_py_res())?; + Ok(ret.to_string()) + } +} +#[pymodule] +fn cozo_py_module(_py: Python<'_>, m: &PyModule) -> PyResult<()> { + m.add_class::()?; + m.add_class::()?; + Ok(()) +}