Working python interface
This commit is contained in:
+24
-16
@@ -1,21 +1,29 @@
|
||||
use std::{ffi::CString, fs, path::Path};
|
||||
|
||||
#[link(name = "python3.13")]
|
||||
extern "C" {
|
||||
fn Py_Initialize();
|
||||
fn PyRun_SimpleString(command: *const i8) -> i32;
|
||||
fn Py_Finalize();
|
||||
pub enum FluxValue {
|
||||
Int(i64),
|
||||
Float(f64),
|
||||
Str(String),
|
||||
Null,
|
||||
// later: List(Vec<FluxValue>), Dict(HashMap<String, FluxValue>)
|
||||
}
|
||||
|
||||
pub fn run_py(path: &Path) {
|
||||
let code = fs::read_to_string(path).expect("Failed to read python script");
|
||||
pub trait Serialize {
|
||||
fn serialize(&self) -> FluxValue;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Py_Initialize();
|
||||
pub trait Deserialize {
|
||||
fn deserialize(v: FluxValue) -> Self;
|
||||
}
|
||||
|
||||
let code = CString::new(code.as_str()).unwrap();
|
||||
PyRun_SimpleString(code.as_ptr());
|
||||
pub trait FluxModule {
|
||||
fn init(&self);
|
||||
fn end(&self);
|
||||
fn call(&self, func: &str, args: Vec<&dyn Serialize>) -> FluxValue;
|
||||
}
|
||||
|
||||
pub mod values;
|
||||
pub mod runtime;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{FluxModule, Serialize, Deserialize};
|
||||
}
|
||||
|
||||
Py_Finalize();
|
||||
}
|
||||
}
|
||||
+5
-3
@@ -1,6 +1,8 @@
|
||||
use std::path::Path;
|
||||
use flux_rt::run_py;
|
||||
use flux_rt::{runtime::python::PythonModule, prelude::*};
|
||||
|
||||
fn main() {
|
||||
run_py(Path::new("bro.py"));
|
||||
let bro = PythonModule("bro.py".to_string());
|
||||
bro.init();
|
||||
|
||||
bro.call("hello", vec![]);
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
pub mod python;
|
||||
@@ -0,0 +1,113 @@
|
||||
use std::{ffi::{CString, c_char}, fs};
|
||||
|
||||
use crate::{FluxModule, FluxValue, Serialize};
|
||||
|
||||
#[link(name = "python3.13")]
|
||||
extern "C" {
|
||||
fn Py_Initialize();
|
||||
fn Py_Finalize();
|
||||
|
||||
fn PyRun_SimpleString(command: *const i8) -> i32;
|
||||
|
||||
fn PyImport_AddModule(name: *const c_char) -> *mut PyObject;
|
||||
fn PyModule_GetDict(module: *mut PyObject) -> *mut PyObject;
|
||||
fn PyDict_GetItemString(dict: *mut PyObject, key: *const c_char) -> *mut PyObject;
|
||||
|
||||
fn PyObject_CallObject(callable: *mut PyObject, args: *mut PyObject) -> *mut PyObject;
|
||||
fn Py_BuildValue(format: *const c_char, ...) -> *mut PyObject;
|
||||
|
||||
fn PyLong_AsLong(obj: *mut PyObject) -> i64;
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct PyObject;
|
||||
|
||||
pub struct PythonModule(pub String);
|
||||
|
||||
impl FluxModule for PythonModule {
|
||||
fn init(&self) {
|
||||
let code = fs::read_to_string(&self.0).expect("Failed to read python script");
|
||||
let code = CString::new(code.as_str()).unwrap();
|
||||
|
||||
unsafe {
|
||||
Py_Initialize();
|
||||
PyRun_SimpleString(code.as_ptr());
|
||||
}
|
||||
}
|
||||
|
||||
fn call(&self, func: &str, args: Vec<&dyn Serialize>) -> FluxValue {
|
||||
use std::ffi::CString;
|
||||
|
||||
unsafe {
|
||||
// 1. Get __main__ module dict
|
||||
let main_str = CString::new("__main__").unwrap();
|
||||
let module = PyImport_AddModule(main_str.as_ptr());
|
||||
let dict = PyModule_GetDict(module);
|
||||
|
||||
// 2. Get function by name
|
||||
let func_name = CString::new(func).unwrap();
|
||||
let func_obj = PyDict_GetItemString(dict, func_name.as_ptr());
|
||||
|
||||
if func_obj.is_null() {
|
||||
panic!("Python function '{}' not found", func);
|
||||
}
|
||||
|
||||
let mut fmt = String::from("(");
|
||||
let values: Vec<FluxValue> = args.iter().map(|a| a.serialize()).collect();
|
||||
|
||||
for v in &values {
|
||||
match v {
|
||||
FluxValue::Int(_) => fmt.push('l'), // long
|
||||
FluxValue::Float(_) => fmt.push('d'), // double
|
||||
FluxValue::Str(_) => fmt.push('s'), // string
|
||||
FluxValue::Null => fmt.push('O'), // PyObject*
|
||||
}
|
||||
}
|
||||
fmt.push(')');
|
||||
|
||||
let fmt_c = CString::new(fmt).unwrap();
|
||||
|
||||
// Call Py_BuildValue with the right arguments
|
||||
let args_obj: *mut PyObject = match values.as_slice() {
|
||||
[FluxValue::Int(a), FluxValue::Int(b)] => {
|
||||
Py_BuildValue(fmt_c.as_ptr(), *a, *b)
|
||||
}
|
||||
[FluxValue::Float(a), FluxValue::Float(b)] => {
|
||||
Py_BuildValue(fmt_c.as_ptr(), *a, *b)
|
||||
}
|
||||
[FluxValue::Str(a), FluxValue::Str(b)] => {
|
||||
let a_c = CString::new(a.as_str()).unwrap();
|
||||
let b_c = CString::new(b.as_str()).unwrap();
|
||||
Py_BuildValue(fmt_c.as_ptr(), a_c.as_ptr(), b_c.as_ptr())
|
||||
}
|
||||
[FluxValue::Int(a)] => {
|
||||
Py_BuildValue(fmt_c.as_ptr(), *a)
|
||||
}
|
||||
[FluxValue::Float(a)] => {
|
||||
Py_BuildValue(fmt_c.as_ptr(), *a)
|
||||
}
|
||||
[FluxValue::Str(a)] => {
|
||||
let a_c = CString::new(a.as_str()).unwrap();
|
||||
Py_BuildValue(fmt_c.as_ptr(), a_c.as_ptr())
|
||||
}
|
||||
_ => std::ptr::null_mut(),
|
||||
};
|
||||
|
||||
let result_obj = PyObject_CallObject(func_obj, args_obj);
|
||||
|
||||
if result_obj.is_null() {
|
||||
FluxValue::Null
|
||||
} else {
|
||||
let value = PyLong_AsLong(result_obj);
|
||||
FluxValue::Int(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn end(&self) {
|
||||
unsafe {
|
||||
Py_Finalize();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
use crate::{Deserialize, FluxValue, Serialize};
|
||||
|
||||
impl Serialize for i64 {
|
||||
fn serialize(&self) -> FluxValue {
|
||||
FluxValue::Int(*self)
|
||||
}
|
||||
}
|
||||
impl Serialize for f64 {
|
||||
fn serialize(&self) -> FluxValue {
|
||||
FluxValue::Float(*self)
|
||||
}
|
||||
}
|
||||
impl Serialize for &str {
|
||||
fn serialize(&self) -> FluxValue {
|
||||
FluxValue::Str(self.to_string())
|
||||
}
|
||||
}
|
||||
impl Deserialize for i64 {
|
||||
fn deserialize(v: FluxValue) -> Self {
|
||||
match v {
|
||||
FluxValue::Int(i) => i,
|
||||
_ => panic!("Expected Int"),
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user