Working python interface

This commit is contained in:
2025-08-24 23:29:13 +02:00
parent 129fd1ae94
commit 0b1450315a
6 changed files with 171 additions and 23 deletions
+3 -4
View File
@@ -1,4 +1,3 @@
def hello():
print("Hello, bro!")
hello()
def hello(name: str) -> str:
print("Hello, bro! " + name)
return "Idk, return value i guess"
+24 -16
View File
@@ -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
View File
@@ -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![]);
}
+1
View File
@@ -0,0 +1 @@
pub mod python;
+113
View File
@@ -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();
}
}
}
+25
View File
@@ -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"),
}
}
}