Skip to content

Commit 4eaf963

Browse files
authored
refactor: extract py_utils as a separate package (#1299)
1 parent bc33ede commit 4eaf963

File tree

14 files changed

+185
-135
lines changed

14 files changed

+185
-135
lines changed

Cargo.lock

Lines changed: 17 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[workspace]
2-
members = ["rust/cocoindex", "rust/utils"]
2+
members = ["rust/*"]
33
resolver = "2"
44

55
[workspace.package]

rust/cocoindex/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ default = ["legacy-states-v0"]
1414
legacy-states-v0 = []
1515

1616
[dependencies]
17-
cocoindex-utils = { path = "../utils" }
17+
cocoindex_utils = { path = "../utils" }
18+
cocoindex_py_utils = { path = "../py_utils" }
1819

1920
pyo3 = { workspace = true }
2021
pythonize = { workspace = true }

rust/cocoindex/src/ops/interface.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1+
use crate::prelude::*;
2+
13
use std::time::SystemTime;
24

35
use crate::base::{schema::*, spec::IndexOptions, value::*};
4-
use crate::prelude::*;
56
use crate::setup;
67
use chrono::TimeZone;
78
use serde::Serialize;
89

910
pub struct FlowInstanceContext {
1011
pub flow_instance_name: String,
1112
pub auth_registry: Arc<AuthRegistry>,
12-
pub py_exec_ctx: Option<Arc<crate::py::PythonExecutionContext>>,
13+
pub py_exec_ctx: Option<Arc<py_utils::PythonExecutionContext>>,
1314
}
1415

1516
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]

rust/cocoindex/src/ops/py_factory.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use crate::{ops::sdk::BatchedFunctionExecutor, prelude::*, py::future::from_py_future};
1+
use crate::{ops::sdk::BatchedFunctionExecutor, prelude::*};
22

33
use pyo3::{
44
Bound, IntoPyObjectExt, Py, PyAny, Python, pyclass, pymethods,
@@ -13,6 +13,7 @@ use crate::{
1313
py::{self, ToResultWithPyTrace},
1414
};
1515
use anyhow::{Result, anyhow};
16+
use py_utils::from_py_future;
1617

1718
#[pyclass(name = "OpArgSchema")]
1819
pub struct PyOpArgSchema {

rust/cocoindex/src/prelude.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ pub(crate) use async_stream::{stream, try_stream};
3535
pub(crate) use log::{debug, error, info, trace, warn};
3636

3737
pub(crate) use derivative::Derivative;
38+
39+
pub(crate) use cocoindex_py_utils as py_utils;

rust/cocoindex/src/py/convert.rs

Lines changed: 1 addition & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -9,52 +9,8 @@ use pyo3::types::PyAny;
99
use pyo3::types::{PyList, PyTuple};
1010
use pyo3::{exceptions::PyException, prelude::*};
1111
use pythonize::{depythonize, pythonize};
12-
use serde::de::DeserializeOwned;
13-
use std::ops::Deref;
1412

15-
use super::{AnyhowIntoPyResult, IntoPyResult};
16-
17-
#[derive(Debug)]
18-
pub struct Pythonized<T>(pub T);
19-
20-
impl<'py, T: DeserializeOwned> FromPyObject<'py> for Pythonized<T> {
21-
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
22-
Ok(Pythonized(depythonize(obj).into_py_result()?))
23-
}
24-
}
25-
26-
impl<'py, T: Serialize> IntoPyObject<'py> for &Pythonized<T> {
27-
type Target = PyAny;
28-
type Output = Bound<'py, PyAny>;
29-
type Error = PyErr;
30-
31-
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
32-
pythonize(py, &self.0).into_py_result()
33-
}
34-
}
35-
36-
impl<'py, T: Serialize> IntoPyObject<'py> for Pythonized<T> {
37-
type Target = PyAny;
38-
type Output = Bound<'py, PyAny>;
39-
type Error = PyErr;
40-
41-
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
42-
(&self).into_pyobject(py)
43-
}
44-
}
45-
46-
impl<T> Pythonized<T> {
47-
pub fn into_inner(self) -> T {
48-
self.0
49-
}
50-
}
51-
52-
impl<T> Deref for Pythonized<T> {
53-
type Target = T;
54-
fn deref(&self) -> &Self::Target {
55-
&self.0
56-
}
57-
}
13+
use py_utils::{AnyhowIntoPyResult, IntoPyResult};
5814

5915
fn basic_value_to_py_object<'py>(
6016
py: Python<'py>,

rust/cocoindex/src/py/mod.rs

Lines changed: 4 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -13,90 +13,14 @@ use crate::service::query_handler::QueryHandlerSpec;
1313
use crate::settings::Settings;
1414
use crate::setup::{self};
1515
use pyo3::IntoPyObjectExt;
16-
use pyo3::types::{PyDict, PyModule, PyString};
17-
use pyo3::{exceptions::PyException, prelude::*};
16+
use pyo3::prelude::*;
17+
use pyo3::types::PyModule;
1818
use pyo3_async_runtimes::tokio::future_into_py;
19-
use std::fmt::Write;
2019
use std::sync::Arc;
2120

2221
mod convert;
2322
pub(crate) use convert::*;
24-
pub mod future;
25-
26-
pub struct PythonExecutionContext {
27-
pub event_loop: Py<PyAny>,
28-
}
29-
30-
impl PythonExecutionContext {
31-
pub fn new(_py: Python<'_>, event_loop: Py<PyAny>) -> Self {
32-
Self { event_loop }
33-
}
34-
}
35-
36-
pub trait ToResultWithPyTrace<T> {
37-
fn to_result_with_py_trace(self, py: Python<'_>) -> anyhow::Result<T>;
38-
}
39-
40-
impl<T> ToResultWithPyTrace<T> for Result<T, PyErr> {
41-
fn to_result_with_py_trace(self, py: Python<'_>) -> anyhow::Result<T> {
42-
match self {
43-
Ok(value) => Ok(value),
44-
Err(err) => {
45-
// Attempt to render a full Python-style traceback including cause/context chain
46-
let full_trace: PyResult<String> = (|| {
47-
let exc = err.value(py);
48-
let traceback = PyModule::import(py, "traceback")?;
49-
let tbe_class = traceback.getattr("TracebackException")?;
50-
let tbe = tbe_class.call_method1("from_exception", (exc,))?;
51-
let kwargs = PyDict::new(py);
52-
kwargs.set_item("chain", true)?;
53-
let lines = tbe.call_method("format", (), Some(&kwargs))?;
54-
let joined = PyString::new(py, "").call_method1("join", (lines,))?;
55-
joined.extract::<String>()
56-
})();
57-
58-
let err_str = match full_trace {
59-
Ok(trace) => format!("Error calling Python function:\n{trace}"),
60-
Err(_) => {
61-
// Fallback: include the PyErr display and available traceback formatting
62-
let mut s = format!("Error calling Python function: {err}");
63-
if let Some(tb) = err.traceback(py) {
64-
write!(&mut s, "\n{}", tb.format()?).ok();
65-
}
66-
s
67-
}
68-
};
69-
70-
Err(anyhow::anyhow!(err_str))
71-
}
72-
}
73-
}
74-
}
75-
pub trait IntoPyResult<T> {
76-
fn into_py_result(self) -> PyResult<T>;
77-
}
78-
79-
impl<T, E: std::error::Error> IntoPyResult<T> for Result<T, E> {
80-
fn into_py_result(self) -> PyResult<T> {
81-
match self {
82-
Ok(value) => Ok(value),
83-
Err(err) => Err(PyException::new_err(format!("{err:?}"))),
84-
}
85-
}
86-
}
87-
88-
pub trait AnyhowIntoPyResult<T> {
89-
fn into_py_result(self) -> PyResult<T>;
90-
}
91-
92-
impl<T> AnyhowIntoPyResult<T> for anyhow::Result<T> {
93-
fn into_py_result(self) -> PyResult<T> {
94-
match self {
95-
Ok(value) => Ok(value),
96-
Err(err) => Err(PyException::new_err(format!("{err:?}"))),
97-
}
98-
}
99-
}
23+
pub(crate) use py_utils::*;
10024

10125
#[pyfunction]
10226
fn set_settings_fn(get_settings_fn: Py<PyAny>) -> PyResult<()> {
@@ -480,7 +404,7 @@ impl Flow {
480404
let task_locals = pyo3_async_runtimes::TaskLocals::new(
481405
py_exec_ctx.event_loop.bind(py).clone(),
482406
);
483-
Ok(future::from_py_future(
407+
Ok(py_utils::from_py_future(
484408
py,
485409
&task_locals,
486410
result_coro.into_bound(py),

rust/py_utils/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "cocoindex_py_utils"
3+
version = { workspace = true }
4+
edition = { workspace = true }
5+
rust-version = { workspace = true }
6+
license = { workspace = true }
7+
8+
[dependencies]
9+
cocoindex_utils = { path = "../utils" }
10+
11+
anyhow = { workspace = true }
12+
futures = { workspace = true }
13+
pyo3 = { workspace = true }
14+
pyo3-async-runtimes = { workspace = true }
15+
pythonize = { workspace = true }
16+
serde = { workspace = true }
17+
log = { workspace = true }

rust/py_utils/src/convert.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use pyo3::prelude::*;
2+
use pythonize::{depythonize, pythonize};
3+
use serde::{Serialize, de::DeserializeOwned};
4+
use std::ops::Deref;
5+
6+
use crate::error::IntoPyResult;
7+
8+
#[derive(Debug)]
9+
pub struct Pythonized<T>(pub T);
10+
11+
impl<'py, T: DeserializeOwned> FromPyObject<'py> for Pythonized<T> {
12+
fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
13+
Ok(Pythonized(depythonize(obj).into_py_result()?))
14+
}
15+
}
16+
17+
impl<'py, T: Serialize> IntoPyObject<'py> for &Pythonized<T> {
18+
type Target = PyAny;
19+
type Output = Bound<'py, PyAny>;
20+
type Error = PyErr;
21+
22+
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
23+
pythonize(py, &self.0).into_py_result()
24+
}
25+
}
26+
27+
impl<'py, T: Serialize> IntoPyObject<'py> for Pythonized<T> {
28+
type Target = PyAny;
29+
type Output = Bound<'py, PyAny>;
30+
type Error = PyErr;
31+
32+
fn into_pyobject(self, py: Python<'py>) -> PyResult<Self::Output> {
33+
(&self).into_pyobject(py)
34+
}
35+
}
36+
37+
impl<T> Pythonized<T> {
38+
pub fn into_inner(self) -> T {
39+
self.0
40+
}
41+
}
42+
43+
impl<T> Deref for Pythonized<T> {
44+
type Target = T;
45+
fn deref(&self) -> &Self::Target {
46+
&self.0
47+
}
48+
}

0 commit comments

Comments
 (0)