Skip to content

Commit fb57ec3

Browse files
authored
Use custom code to convert Rust values to Python so bytes are bytes (#75)
Previously we're using the `Serialize` so bytes become base64 strings
1 parent 19cdd90 commit fb57ec3

File tree

1 file changed

+67
-3
lines changed

1 file changed

+67
-3
lines changed

src/ops/py_factory.rs

Lines changed: 67 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use futures::FutureExt;
66
use pyo3::{
77
exceptions::PyException,
88
pyclass, pymethods,
9-
types::{IntoPyDict, PyAnyMethods, PyString, PyTuple},
9+
types::{IntoPyDict, PyAnyMethods, PyList, PyString, PyTuple},
1010
Bound, IntoPyObjectExt, Py, PyAny, PyResult, Python,
1111
};
1212
use pythonize::pythonize;
@@ -22,6 +22,70 @@ use super::sdk::{
2222
ExecutorFuture, FlowInstanceContext, SimpleFunctionExecutor, SimpleFunctionFactory,
2323
};
2424

25+
fn basic_value_to_py_object<'py>(
26+
py: Python<'py>,
27+
v: &value::BasicValue,
28+
) -> PyResult<Bound<'py, PyAny>> {
29+
let result = match v {
30+
value::BasicValue::Bytes(v) => v.into_bound_py_any(py)?,
31+
value::BasicValue::Str(v) => v.into_bound_py_any(py)?,
32+
value::BasicValue::Bool(v) => v.into_bound_py_any(py)?,
33+
value::BasicValue::Int64(v) => v.into_bound_py_any(py)?,
34+
value::BasicValue::Float32(v) => v.into_bound_py_any(py)?,
35+
value::BasicValue::Float64(v) => v.into_bound_py_any(py)?,
36+
value::BasicValue::Vector(v) => v
37+
.iter()
38+
.map(|v| basic_value_to_py_object(py, v))
39+
.collect::<PyResult<Vec<_>>>()?
40+
.into_bound_py_any(py)?,
41+
_ => {
42+
return Err(PyException::new_err(format!(
43+
"unsupported value type: {}",
44+
v.kind()
45+
)))
46+
}
47+
};
48+
Ok(result)
49+
}
50+
51+
fn field_values_to_py_object<'py, 'a>(
52+
py: Python<'py>,
53+
values: impl Iterator<Item = &'a value::Value>,
54+
) -> PyResult<Bound<'py, PyAny>> {
55+
let fields = values
56+
.map(|v| value_to_py_object(py, v))
57+
.collect::<PyResult<Vec<_>>>()?;
58+
Ok(PyTuple::new(py, fields)?.into_any())
59+
}
60+
61+
fn value_to_py_object<'py>(py: Python<'py>, v: &value::Value) -> PyResult<Bound<'py, PyAny>> {
62+
let result = match v {
63+
value::Value::Null => py.None().into_bound(py),
64+
value::Value::Basic(v) => basic_value_to_py_object(py, v)?,
65+
value::Value::Struct(v) => field_values_to_py_object(py, v.fields.iter())?,
66+
value::Value::Collection(v) | value::Value::List(v) => {
67+
let rows = v
68+
.iter()
69+
.map(|v| field_values_to_py_object(py, v.0.fields.iter()))
70+
.collect::<PyResult<Vec<_>>>()?;
71+
PyList::new(py, rows)?.into_any()
72+
}
73+
value::Value::Table(v) => {
74+
let rows = v
75+
.iter()
76+
.map(|(k, v)| {
77+
field_values_to_py_object(
78+
py,
79+
std::iter::once(&value::Value::from(k.clone())).chain(v.0.fields.iter()),
80+
)
81+
})
82+
.collect::<PyResult<Vec<_>>>()?;
83+
PyList::new(py, rows)?.into_any()
84+
}
85+
};
86+
Ok(result)
87+
}
88+
2589
fn basic_value_from_py_object<'py>(
2690
typ: &schema::BasicValueType,
2791
v: &Bound<'py, PyAny>,
@@ -159,7 +223,7 @@ impl SimpleFunctionExecutor for Arc<PyFunctionExecutor> {
159223
Python::with_gil(|py| -> Result<_> {
160224
let mut args = Vec::with_capacity(self.num_positional_args);
161225
for v in input[0..self.num_positional_args].iter() {
162-
args.push(pythonize(py, v)?);
226+
args.push(value_to_py_object(py, v)?);
163227
}
164228

165229
let kwargs = if self.kw_args_names.is_empty() {
@@ -171,7 +235,7 @@ impl SimpleFunctionExecutor for Arc<PyFunctionExecutor> {
171235
.iter()
172236
.zip(input[self.num_positional_args..].iter())
173237
{
174-
kwargs.push((name.bind(py), pythonize(py, v)?));
238+
kwargs.push((name.bind(py), value_to_py_object(py, v)?));
175239
}
176240
Some(kwargs)
177241
};

0 commit comments

Comments
 (0)