Skip to content

Commit 372d3ec

Browse files
committed
Update dependencies and improve PyO3 integration
1 parent 9474fdf commit 372d3ec

File tree

4 files changed

+56
-41
lines changed

4 files changed

+56
-41
lines changed

CHANGELOG.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737
- cel-interpreter: 0.9.0 → 0.10.0 (major version update with breaking changes)
3838
- log: 0.4.22 → 0.4.27
3939
- chrono: 0.4.38 → 0.4.41
40-
- PyO3 and related crates: Updated to resolve deprecation warnings
40+
- pyo3: 0.22.6 → 0.25.0 (major API upgrade with IntoPyObject migration)
41+
- pyo3-log: 0.11.0 → 0.12.1 (compatible with pyo3 0.25.0)
42+
43+
### Notes
44+
- **PyO3 0.25.0 Migration**: Successfully migrated from deprecated `IntoPy` trait to new `IntoPyObject` API
45+
- **API Improvements**: New conversion system provides better error handling and type safety
46+
- **Build Status**: All 120 tests pass with current dependency versions
4147

Cargo.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ name = "cel"
99
crate-type = ["cdylib"]
1010

1111
[dependencies]
12-
pyo3 = { version = "0.22.6", features = ["chrono", "gil-refs", "py-clone"]}
12+
pyo3 = { version = "0.25.0", features = ["chrono", "py-clone"]}
1313
cel-interpreter = { version = "0.10.0", features = ["chrono", "json", "regex"] }
1414
log = "0.4.27"
15-
pyo3-log = "0.11.0"
15+
pyo3-log = "0.12.1"
1616
chrono = { version = "0.4.41", features = ["serde"] }

src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ impl Context {
6262

6363
if value.is_callable() {
6464
// Value is a function, add it to the functions hashmap
65-
let py_function = value.to_object(value.py());
65+
let py_function = value.unbind();
6666
self.functions.insert(key, py_function);
6767
} else {
6868
// Value is a variable, add it to the variables hashmap

src/lib.rs

Lines changed: 46 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ use cel_interpreter::{ExecutionError, Program, Value};
55
use log::{debug, warn};
66
use pyo3::exceptions::PyValueError;
77
use pyo3::prelude::*;
8+
use pyo3::BoundObject;
89
use std::panic;
910

1011
use chrono::{DateTime, Duration as ChronoDuration, Offset, TimeZone};
11-
use pyo3::types::{PyBytes, PyDict, PyList, PyTuple};
12+
use pyo3::types::{PyBool, PyBytes, PyDict, PyList, PyTuple};
1213

1314
use std::collections::HashMap;
1415
use std::error::Error;
@@ -18,57 +19,62 @@ use std::sync::Arc;
1819
#[derive(Debug)]
1920
struct RustyCelType(Value);
2021

21-
impl IntoPy<PyObject> for RustyCelType {
22-
fn into_py(self, py: Python<'_>) -> PyObject {
23-
// Just use the native rust type's existing
24-
// IntoPy implementation
25-
match self {
22+
impl<'py> IntoPyObject<'py> for RustyCelType {
23+
type Target = PyAny;
24+
type Output = Bound<'py, Self::Target>;
25+
type Error = PyErr;
26+
27+
fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
28+
let obj = match self {
2629
// Primitive Types
27-
RustyCelType(Value::Null) => py.None(),
28-
RustyCelType(Value::Bool(b)) => b.into_py(py),
29-
RustyCelType(Value::Int(i64)) => i64.into_py(py),
30-
RustyCelType(Value::UInt(u64)) => u64.into_py(py),
31-
RustyCelType(Value::Float(f)) => f.into_py(py),
30+
RustyCelType(Value::Null) => py.None().into_bound(py),
31+
RustyCelType(Value::Bool(b)) => {
32+
PyBool::new(py, b).into_bound().into_any()
33+
}
34+
RustyCelType(Value::Int(i64)) => i64.into_pyobject(py)?.into_any(),
35+
RustyCelType(Value::UInt(u64)) => u64.into_pyobject(py)?.into_any(),
36+
RustyCelType(Value::Float(f)) => f.into_pyobject(py)?.into_any(),
3237
RustyCelType(Value::Timestamp(ts)) => {
3338
debug!("Converting a fixed offset datetime to python type");
34-
ts.into_py(py)
39+
ts.into_pyobject(py)?.into_any()
3540
}
36-
RustyCelType(Value::Duration(d)) => d.into_py(py),
37-
RustyCelType(Value::String(s)) => s.as_ref().to_string().into_py(py),
41+
RustyCelType(Value::Duration(d)) => d.into_pyobject(py)?.into_any(),
42+
RustyCelType(Value::String(s)) => s.as_ref().to_string().into_pyobject(py)?.into_any(),
3843
RustyCelType(Value::List(val)) => {
39-
let list = val
40-
.as_ref()
41-
.into_iter()
42-
.map(|v| RustyCelType(v.clone()).into_py(py))
43-
.collect::<Vec<PyObject>>();
44-
list.into_py(py)
44+
let list = PyList::empty(py);
45+
for v in val.as_ref().into_iter() {
46+
let item = RustyCelType(v.clone()).into_pyobject(py)?;
47+
list.append(&item)?;
48+
}
49+
list.into_any()
4550
}
46-
RustyCelType(Value::Bytes(val)) => PyBytes::new_bound(py, &val).into_py(py),
51+
RustyCelType(Value::Bytes(val)) => PyBytes::new(py, &val).into_any(),
4752

4853
RustyCelType(Value::Map(val)) => {
4954
// Create a PyDict with the converted Python key and values.
50-
let python_dict = PyDict::new_bound(py);
55+
let python_dict = PyDict::new(py);
5156

52-
val.map.as_ref().into_iter().for_each(|(k, v)| {
57+
for (k, v) in val.map.as_ref().into_iter() {
5358
// Key is an enum with String, Uint, Int and Bool variants. Value is any RustyCelType
5459
let key = match k {
55-
Key::String(s) => s.as_ref().into_py(py),
56-
Key::Uint(u64) => u64.into_py(py),
57-
Key::Int(i64) => i64.into_py(py),
58-
Key::Bool(b) => b.into_py(py),
60+
Key::String(s) => s.as_ref().into_pyobject(py)?.into_any(),
61+
Key::Uint(u64) => u64.into_pyobject(py)?.into_any(),
62+
Key::Int(i64) => i64.into_pyobject(py)?.into_any(),
63+
Key::Bool(b) => {
64+
PyBool::new(py, *b).into_bound().into_any()
65+
}
5966
};
60-
let value = RustyCelType(v.clone()).into_py(py);
61-
python_dict
62-
.set_item(key, value)
63-
.expect("Failed to set item in Python dict");
64-
});
67+
let value = RustyCelType(v.clone()).into_pyobject(py)?;
68+
python_dict.set_item(&key, &value)?;
69+
}
6570

66-
python_dict.into()
71+
python_dict.into_any()
6772
}
6873

6974
// Turn everything else into a String:
70-
nonprimitive => format!("{:?}", nonprimitive).into_py(py),
71-
}
75+
nonprimitive => format!("{:?}", nonprimitive).into_pyobject(py)?.into_any(),
76+
};
77+
Ok(obj)
7278
}
7379
}
7480

@@ -435,10 +441,13 @@ fn evaluate(src: String, evaluation_context: Option<&Bound<'_, PyAny>>) -> PyRes
435441
let mut py_args = Vec::new();
436442
for arg_expr in &ftx.args {
437443
let arg_value = ftx.ptx.resolve(arg_expr)?;
438-
let py_arg = RustyCelType(arg_value).into_py(py);
444+
let py_arg = RustyCelType(arg_value).into_pyobject(py)
445+
.map_err(|e| ExecutionError::function_error("argument_conversion", format!("Failed to convert argument: {}", e)))?
446+
.into_any().unbind();
439447
py_args.push(py_arg);
440448
}
441-
let py_args = PyTuple::new_bound(py, py_args);
449+
let py_args = PyTuple::new(py, py_args)
450+
.map_err(|e| ExecutionError::function_error("tuple_creation", format!("Failed to create tuple: {}", e)))?;
442451

443452
// Call the Python function
444453
let py_result = py_function.call1(py, py_args).map_err(|e| {

0 commit comments

Comments
 (0)