allow calling simple Python functions from Rhai
This commit is contained in:
170
src/lib.rs
170
src/lib.rs
@@ -81,6 +81,7 @@ fn python_to_dynamic(py: Python<'_>, obj: &Bound<'_, PyAny>) -> PyResult<Dynamic
|
||||
struct RhaiEngine {
|
||||
engine: Engine,
|
||||
scope: Scope<'static>,
|
||||
py_functions: std::collections::HashMap<String, PyObject>,
|
||||
}
|
||||
|
||||
#[pymethods]
|
||||
@@ -90,6 +91,7 @@ impl RhaiEngine {
|
||||
Self {
|
||||
engine: Engine::new(),
|
||||
scope: Scope::new(),
|
||||
py_functions: std::collections::HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,11 +152,179 @@ impl RhaiEngine {
|
||||
.map(|(name, _, _)| name.to_string())
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Register a Python function to be callable from Rhai using signature inspection
|
||||
fn register_fn(&mut self, py: Python<'_>, name: &str, func: PyObject) -> PyResult<()> {
|
||||
// Get the function signature using inspect.signature()
|
||||
let inspect_module = py.import("inspect")?;
|
||||
let signature_func = inspect_module.getattr("signature")?;
|
||||
let sig = signature_func.call1((&func,))?;
|
||||
let parameters = sig.getattr("parameters")?;
|
||||
let param_count = parameters.len()?;
|
||||
|
||||
// Store the function
|
||||
self.py_functions
|
||||
.insert(name.to_string(), func.clone_ref(py));
|
||||
|
||||
let func_name = name.to_string();
|
||||
|
||||
// Register the function with the appropriate arity
|
||||
match param_count {
|
||||
0 => {
|
||||
self.engine.register_fn(name, {
|
||||
let func = func.clone_ref(py);
|
||||
let name = func_name.clone();
|
||||
move || -> Result<Dynamic, Box<rhai::EvalAltResult>> {
|
||||
Python::with_gil(|py| {
|
||||
let result = func
|
||||
.call0(py)
|
||||
.map_err(|e| format!("Python function '{}' error: {}", name, e))?;
|
||||
python_to_dynamic(py, &result.bind(py))
|
||||
.map_err(|e| format!("Type conversion error: {}", e).into())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
1 => {
|
||||
self.engine.register_fn(name, {
|
||||
let func = func.clone_ref(py);
|
||||
let name = func_name.clone();
|
||||
move |arg1: Dynamic| -> Result<Dynamic, Box<rhai::EvalAltResult>> {
|
||||
Python::with_gil(|py| {
|
||||
let py_arg1 = dynamic_to_python(py, arg1)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let result = func
|
||||
.call1(py, (py_arg1,))
|
||||
.map_err(|e| format!("Python function '{}' error: {}", name, e))?;
|
||||
python_to_dynamic(py, &result.bind(py))
|
||||
.map_err(|e| format!("Type conversion error: {}", e).into())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
2 => {
|
||||
self.engine.register_fn(name, {
|
||||
let func = func.clone_ref(py);
|
||||
let name = func_name.clone();
|
||||
move |arg1: Dynamic,
|
||||
arg2: Dynamic|
|
||||
-> Result<Dynamic, Box<rhai::EvalAltResult>> {
|
||||
Python::with_gil(|py| {
|
||||
let py_arg1 = dynamic_to_python(py, arg1)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg2 = dynamic_to_python(py, arg2)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let result = func
|
||||
.call1(py, (py_arg1, py_arg2))
|
||||
.map_err(|e| format!("Python function '{}' error: {}", name, e))?;
|
||||
python_to_dynamic(py, &result.bind(py))
|
||||
.map_err(|e| format!("Type conversion error: {}", e).into())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
3 => {
|
||||
self.engine.register_fn(name, {
|
||||
let func = func.clone_ref(py);
|
||||
let name = func_name.clone();
|
||||
move |arg1: Dynamic,
|
||||
arg2: Dynamic,
|
||||
arg3: Dynamic|
|
||||
-> Result<Dynamic, Box<rhai::EvalAltResult>> {
|
||||
Python::with_gil(|py| {
|
||||
let py_arg1 = dynamic_to_python(py, arg1)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg2 = dynamic_to_python(py, arg2)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg3 = dynamic_to_python(py, arg3)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let result = func
|
||||
.call1(py, (py_arg1, py_arg2, py_arg3))
|
||||
.map_err(|e| format!("Python function '{}' error: {}", name, e))?;
|
||||
python_to_dynamic(py, &result.bind(py))
|
||||
.map_err(|e| format!("Type conversion error: {}", e).into())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
4 => {
|
||||
self.engine.register_fn(name, {
|
||||
let func = func.clone_ref(py);
|
||||
let name = func_name.clone();
|
||||
move |arg1: Dynamic,
|
||||
arg2: Dynamic,
|
||||
arg3: Dynamic,
|
||||
arg4: Dynamic|
|
||||
-> Result<Dynamic, Box<rhai::EvalAltResult>> {
|
||||
Python::with_gil(|py| {
|
||||
let py_arg1 = dynamic_to_python(py, arg1)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg2 = dynamic_to_python(py, arg2)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg3 = dynamic_to_python(py, arg3)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg4 = dynamic_to_python(py, arg4)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let result = func
|
||||
.call1(py, (py_arg1, py_arg2, py_arg3, py_arg4))
|
||||
.map_err(|e| format!("Python function '{}' error: {}", name, e))?;
|
||||
python_to_dynamic(py, &result.bind(py))
|
||||
.map_err(|e| format!("Type conversion error: {}", e).into())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
5 => {
|
||||
self.engine.register_fn(name, {
|
||||
let func = func.clone_ref(py);
|
||||
let name = func_name.clone();
|
||||
move |arg1: Dynamic,
|
||||
arg2: Dynamic,
|
||||
arg3: Dynamic,
|
||||
arg4: Dynamic,
|
||||
arg5: Dynamic|
|
||||
-> Result<Dynamic, Box<rhai::EvalAltResult>> {
|
||||
Python::with_gil(|py| {
|
||||
let py_arg1 = dynamic_to_python(py, arg1)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg2 = dynamic_to_python(py, arg2)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg3 = dynamic_to_python(py, arg3)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg4 = dynamic_to_python(py, arg4)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let py_arg5 = dynamic_to_python(py, arg5)
|
||||
.map_err(|e| format!("Type conversion error: {}", e))?;
|
||||
let result = func
|
||||
.call1(py, (py_arg1, py_arg2, py_arg3, py_arg4, py_arg5))
|
||||
.map_err(|e| format!("Python function '{}' error: {}", name, e))?;
|
||||
python_to_dynamic(py, &result.bind(py))
|
||||
.map_err(|e| format!("Type conversion error: {}", e).into())
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
_ => {
|
||||
return Err(PyRuntimeError::new_err(format!(
|
||||
"Function '{}' has {} parameters, but only 0-5 parameters are supported",
|
||||
name, param_count
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// List registered Python functions
|
||||
fn list_python_functions(&self) -> Vec<String> {
|
||||
self.py_functions.keys().cloned().collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A Python module implemented in Rust using PyO3
|
||||
#[pymodule]
|
||||
fn rhai_python(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
||||
m.add_class::<RhaiEngine>()?;
|
||||
m.add("__version__", "0.1.1")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user