From 357cf7863c776f69040a2f0e7b15f187d6c0fea5 Mon Sep 17 00:00:00 2001 From: gelox Date: Mon, 14 Nov 2022 23:50:18 +0100 Subject: [PATCH 1/4] Add example for calling between rust and python Add an example which illustrates how to call between rust and python. Most importantly there no examples which illustrate how to call rust from python and it is not obvious how to do this. --- examples/call_between_rust_and_python.py | 13 ++++ examples/call_between_rust_and_python.rs | 88 ++++++++++++++++++++++++ 2 files changed, 101 insertions(+) create mode 100644 examples/call_between_rust_and_python.py create mode 100644 examples/call_between_rust_and_python.rs diff --git a/examples/call_between_rust_and_python.py b/examples/call_between_rust_and_python.py new file mode 100644 index 000000000..5183bc679 --- /dev/null +++ b/examples/call_between_rust_and_python.py @@ -0,0 +1,13 @@ +from rust_py_module import RustStruct, rust_function + +class PythonPerson: + def __init__(self, name): + self.name = name + +def python_callback(): + python_person = PythonPerson("Peter Python") + rust_object = rust_function(42, "This is a python string", python_person) + rust_object.print_in_rust_from_python() + +def take_string(string): + print("Calling python function from rust with string: " + string) diff --git a/examples/call_between_rust_and_python.rs b/examples/call_between_rust_and_python.rs new file mode 100644 index 000000000..469acb613 --- /dev/null +++ b/examples/call_between_rust_and_python.rs @@ -0,0 +1,88 @@ +use rustpython_vm::{ + builtins::PyStr, + function::{FuncArgs, KwArgs, PosArgs}, + pyclass, pymodule, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, + VirtualMachine, +}; + +pub(crate) use rust_py_module::make_module; + +pub fn main() { + let interp = rustpython_vm::Interpreter::with_init(Default::default(), |vm| { + vm.add_native_modules(rustpython_stdlib::get_module_inits()); + vm.add_native_module("rust_py_module".to_owned(), Box::new(make_module)); + }); + + interp.enter(|vm| { + vm.insert_sys_path(vm.new_pyobj("examples")) + .expect("add path"); + + let module = vm.import("call_between_rust_and_python", None, 0).unwrap(); + let init_fn = module.get_attr("python_callback", vm).unwrap(); + + vm.invoke(&init_fn, ()).unwrap(); + + let pystr = PyObjectRef::from(PyStr::new_ref( + unsafe { + PyStr::new_ascii_unchecked(String::from("Rust string sent to python").into_bytes()) + }, + vm.as_ref(), + )); + let take_string_args = FuncArgs::new(PosArgs::new(vec![pystr]), KwArgs::default()); + let take_string_fn = module.get_attr("take_string", vm).unwrap(); + + vm.invoke(&take_string_fn, take_string_args).unwrap(); + }) +} + +#[pymodule] +mod rust_py_module { + use super::*; + + #[pyfunction] + fn rust_function( + num: i32, + s: String, + python_person: PyObjectRef, + vm: &VirtualMachine, + ) -> PyResult { + println!( + "Calling standalone rust function from python passing args: +num: {}, +string: {}, +python_person.name: {}", + num, + s, + python_person.try_into_value::(vm).unwrap().name + ); + Ok(RustStruct) + } + + #[pyattr] + #[pyclass(module = "rust_py_module", name = "RustStruct")] + #[derive(Debug, PyPayload)] + struct RustStruct; + + #[pyclass] + impl RustStruct { + #[pymethod] + fn print_in_rust_from_python(&self) { + println!("Calling a rust method from python"); + } + } + + struct PythonPerson { + name: String, + } + + impl TryFromBorrowedObject for PythonPerson { + fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult { + let name = obj + .get_attr("name", vm) + .unwrap() + .try_into_value::(vm) + .unwrap(); + Ok(PythonPerson { name }) + } + } +} From 88299ef0bba85796267592b55a7d2cfb8de79096 Mon Sep 17 00:00:00 2001 From: gelox Date: Tue, 15 Nov 2022 15:58:13 +0100 Subject: [PATCH 2/4] Fix review comments --- examples/call_between_rust_and_python.rs | 27 ++++++------------------ 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/examples/call_between_rust_and_python.rs b/examples/call_between_rust_and_python.rs index 469acb613..867d82433 100644 --- a/examples/call_between_rust_and_python.rs +++ b/examples/call_between_rust_and_python.rs @@ -1,7 +1,5 @@ use rustpython_vm::{ - builtins::PyStr, - function::{FuncArgs, KwArgs, PosArgs}, - pyclass, pymodule, PyObject, PyObjectRef, PyPayload, PyResult, TryFromBorrowedObject, + pyclass, pymodule, PyObject, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine, }; @@ -19,19 +17,10 @@ pub fn main() { let module = vm.import("call_between_rust_and_python", None, 0).unwrap(); let init_fn = module.get_attr("python_callback", vm).unwrap(); - vm.invoke(&init_fn, ()).unwrap(); - let pystr = PyObjectRef::from(PyStr::new_ref( - unsafe { - PyStr::new_ascii_unchecked(String::from("Rust string sent to python").into_bytes()) - }, - vm.as_ref(), - )); - let take_string_args = FuncArgs::new(PosArgs::new(vec![pystr]), KwArgs::default()); let take_string_fn = module.get_attr("take_string", vm).unwrap(); - - vm.invoke(&take_string_fn, take_string_args).unwrap(); + vm.invoke(&take_string_fn, (String::from("Rust string sent to python"),)).unwrap(); }) } @@ -43,8 +32,8 @@ mod rust_py_module { fn rust_function( num: i32, s: String, - python_person: PyObjectRef, - vm: &VirtualMachine, + python_person: PythonPerson, + _vm: &VirtualMachine, ) -> PyResult { println!( "Calling standalone rust function from python passing args: @@ -53,7 +42,7 @@ string: {}, python_person.name: {}", num, s, - python_person.try_into_value::(vm).unwrap().name + python_person.name ); Ok(RustStruct) } @@ -78,10 +67,8 @@ python_person.name: {}", impl TryFromBorrowedObject for PythonPerson { fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult { let name = obj - .get_attr("name", vm) - .unwrap() - .try_into_value::(vm) - .unwrap(); + .get_attr("name", vm)? + .try_into_value::(vm)?; Ok(PythonPerson { name }) } } From 445cb6cc8bd45e5f31a964fad3ebd65797958c07 Mon Sep 17 00:00:00 2001 From: gelox Date: Wed, 16 Nov 2022 12:06:23 +0100 Subject: [PATCH 3/4] Format --- examples/call_between_rust_and_python.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/examples/call_between_rust_and_python.rs b/examples/call_between_rust_and_python.rs index 867d82433..2bcc2737f 100644 --- a/examples/call_between_rust_and_python.rs +++ b/examples/call_between_rust_and_python.rs @@ -1,6 +1,5 @@ use rustpython_vm::{ - pyclass, pymodule, PyObject, PyPayload, PyResult, TryFromBorrowedObject, - VirtualMachine, + pyclass, pymodule, PyObject, PyPayload, PyResult, TryFromBorrowedObject, VirtualMachine, }; pub(crate) use rust_py_module::make_module; @@ -20,7 +19,11 @@ pub fn main() { vm.invoke(&init_fn, ()).unwrap(); let take_string_fn = module.get_attr("take_string", vm).unwrap(); - vm.invoke(&take_string_fn, (String::from("Rust string sent to python"),)).unwrap(); + vm.invoke( + &take_string_fn, + (String::from("Rust string sent to python"),), + ) + .unwrap(); }) } @@ -40,9 +43,7 @@ mod rust_py_module { num: {}, string: {}, python_person.name: {}", - num, - s, - python_person.name + num, s, python_person.name ); Ok(RustStruct) } @@ -66,9 +67,7 @@ python_person.name: {}", impl TryFromBorrowedObject for PythonPerson { fn try_from_borrowed_object(vm: &VirtualMachine, obj: &PyObject) -> PyResult { - let name = obj - .get_attr("name", vm)? - .try_into_value::(vm)?; + let name = obj.get_attr("name", vm)?.try_into_value::(vm)?; Ok(PythonPerson { name }) } } From 340816d4351a94d089568981ed63306c43b8df45 Mon Sep 17 00:00:00 2001 From: gelox Date: Mon, 21 Nov 2022 21:47:05 +0100 Subject: [PATCH 4/4] Add direct member access to example In the example demonstrating how to call between rust and python add direct member access. --- examples/call_between_rust_and_python.py | 1 + examples/call_between_rust_and_python.rs | 24 ++++++++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/examples/call_between_rust_and_python.py b/examples/call_between_rust_and_python.py index 5183bc679..60335d81e 100644 --- a/examples/call_between_rust_and_python.py +++ b/examples/call_between_rust_and_python.py @@ -7,6 +7,7 @@ class PythonPerson: def python_callback(): python_person = PythonPerson("Peter Python") rust_object = rust_function(42, "This is a python string", python_person) + print("Printing member 'numbers' from rust struct: ", rust_object.numbers) rust_object.print_in_rust_from_python() def take_string(string): diff --git a/examples/call_between_rust_and_python.rs b/examples/call_between_rust_and_python.rs index 2bcc2737f..be93fb971 100644 --- a/examples/call_between_rust_and_python.rs +++ b/examples/call_between_rust_and_python.rs @@ -30,6 +30,7 @@ pub fn main() { #[pymodule] mod rust_py_module { use super::*; + use rustpython_vm::{builtins::PyList, convert::ToPyObject, PyObjectRef}; #[pyfunction] fn rust_function( @@ -45,16 +46,35 @@ string: {}, python_person.name: {}", num, s, python_person.name ); - Ok(RustStruct) + Ok(RustStruct { + numbers: NumVec(vec![1, 2, 3, 4]), + }) + } + + #[derive(Debug, Clone)] + struct NumVec(Vec); + + impl ToPyObject for NumVec { + fn to_pyobject(self, vm: &VirtualMachine) -> PyObjectRef { + let list = self.0.into_iter().map(|e| vm.new_pyobj(e)).collect(); + PyList::new_ref(list, vm.as_ref()).to_pyobject(vm) + } } #[pyattr] #[pyclass(module = "rust_py_module", name = "RustStruct")] #[derive(Debug, PyPayload)] - struct RustStruct; + struct RustStruct { + numbers: NumVec, + } #[pyclass] impl RustStruct { + #[pygetset] + fn numbers(&self) -> NumVec { + self.numbers.clone() + } + #[pymethod] fn print_in_rust_from_python(&self) { println!("Calling a rust method from python");