Add support for exposing rust functions to python
This commit is contained in:
parent
60360fa03f
commit
f8e24a870c
|
@ -1,4 +1,3 @@
|
|||
/target
|
||||
/Cargo.lock
|
||||
/testmodule.so
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
#[macro_use] extern crate cpython;
|
||||
|
||||
use cpython::{PythonObject, ObjectProtocol, PyModule, Python};
|
||||
|
||||
fn main() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let sys = PyModule::import(py, cstr!("sys")).unwrap();
|
||||
let path: String = sys.as_object().getattr("version").unwrap().extract().unwrap();
|
||||
println!("Hello Python {}", path);
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
stamps
|
||||
*.out
|
||||
*.so
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
TARGETDIR=../target/debug
|
||||
rwildcard=$(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all:
|
||||
|
||||
clean:
|
||||
-rm *.so
|
||||
-rm *.out
|
||||
-rm -r stamps
|
||||
|
||||
stamps:
|
||||
mkdir stamps
|
||||
|
||||
stamps/rust-cpython: $(call rwildcard,../src,*.rs) Makefile | stamps
|
||||
cd .. && cargo build
|
||||
touch $@
|
||||
|
||||
%.so: %.rs stamps/rust-cpython
|
||||
rustc $< -L $(TARGETDIR) -L $(TARGETDIR)/deps -o $@
|
||||
|
||||
hello.out: hello.so
|
||||
python -c "import hello; hello.run(hello.val())" 2>&1 | tee $@
|
||||
|
||||
all: stamps/test-hello
|
||||
stamps/test-hello: hello.out
|
||||
@grep "Rust says: Hello Python!" hello.out >/dev/null
|
||||
@grep "Rust got 42" hello.out >/dev/null
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
#![crate_type = "dylib"]
|
||||
|
||||
#[macro_use] extern crate cpython;
|
||||
|
||||
use cpython::{PyObject, PyResult, PyModule, Python, PyTuple};
|
||||
|
||||
py_module_initializer!("hello", inithello, |py, m| {
|
||||
try!(m.add(cstr!("__doc__"), "Module documentation string"));
|
||||
try!(m.add(cstr!("run"), py_func!(py, run)));
|
||||
try!(add_val(py, &m));
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fn run<'p>(py: Python<'p>, args: &PyTuple<'p>) -> PyResult<'p, PyObject<'p>> {
|
||||
println!("Rust says: Hello Python!");
|
||||
for arg in args {
|
||||
println!("Rust got {}", arg);
|
||||
}
|
||||
Ok(py.None())
|
||||
}
|
||||
|
||||
fn val<'p>(py: Python<'p>, args: &PyTuple<'p>) -> PyResult<'p, i32> {
|
||||
Ok(42)
|
||||
}
|
||||
|
||||
// Workaround for Rust #24561
|
||||
fn add_val<'p>(py: Python<'p>, m: &PyModule<'p>) -> PyResult<'p, ()> {
|
||||
m.add(cstr!("val"), py_func!(py, val))
|
||||
}
|
||||
|
10
src/err.rs
10
src/err.rs
|
@ -207,6 +207,16 @@ pub unsafe fn result_from_owned_ptr(py : Python, p : *mut ffi::PyObject) -> PyRe
|
|||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub unsafe fn from_owned_ptr_or_panic(py : Python, p : *mut ffi::PyObject) -> PyObject {
|
||||
if p.is_null() {
|
||||
ffi::PyErr_Print();
|
||||
panic!("Python API called failed");
|
||||
} else {
|
||||
PyObject::from_owned_ptr(py, p)
|
||||
}
|
||||
}
|
||||
|
||||
/// Construct PyObject from the result of a python FFI call that returns a borrowed reference.
|
||||
/// Returns Err(PyErr) if the pointer is null.
|
||||
/// Unsafe because the pointer might be invalid.
|
||||
|
|
45
src/lib.rs
45
src/lib.rs
|
@ -12,9 +12,9 @@ pub use ffi::Py_ssize_t;
|
|||
pub use err::{PyErr, PyResult};
|
||||
pub use objects::*;
|
||||
pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject};
|
||||
pub use pythonrun::{GILGuard, prepare_freethreaded_python};
|
||||
pub use conversion::{FromPyObject, ToPyObject};
|
||||
pub use objectprotocol::{ObjectProtocol};
|
||||
pub use std::ffi::CStr;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! cstr(
|
||||
|
@ -33,6 +33,14 @@ mod objects;
|
|||
mod objectprotocol;
|
||||
mod pythonrun;
|
||||
|
||||
/// Private re-exports for use in macros.
|
||||
pub mod _detail {
|
||||
pub use ffi;
|
||||
pub use libc;
|
||||
pub use python::ToPythonPointer;
|
||||
pub use err::from_owned_ptr_or_panic;
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! py_module_initializer {
|
||||
($name: tt, $init_funcname: ident, $init: expr) => {
|
||||
|
@ -47,6 +55,41 @@ macro_rules! py_module_initializer {
|
|||
}
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! py_func {
|
||||
($py: expr, $f: expr) => ({
|
||||
unsafe extern "C" fn wrap_py_func
|
||||
(_slf: *mut $crate::_detail::ffi::PyObject, args: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject {
|
||||
let py = $crate::Python::assume_gil_acquired();
|
||||
let args = $crate::PyObject::from_borrowed_ptr(py, args);
|
||||
let args: &PyTuple = $crate::PythonObject::unchecked_downcast_borrow_from(&args);
|
||||
let result = match $f(py, args) {
|
||||
Ok(val) => $crate::ToPyObject::into_py_object(val, py),
|
||||
Err(e) => Err(e)
|
||||
};
|
||||
match result {
|
||||
Ok(val) => $crate::_detail::ToPythonPointer::steal_ptr(val),
|
||||
Err(e) => { e.restore(); ::std::ptr::null_mut() }
|
||||
}
|
||||
}
|
||||
static mut method_def: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
|
||||
//ml_name: bytes!(stringify!($f), "\0"),
|
||||
ml_name: b"<rust function>\0" as *const u8 as *const $crate::_detail::libc::c_char,
|
||||
ml_meth: Some(wrap_py_func),
|
||||
ml_flags: $crate::_detail::ffi::METH_VARARGS,
|
||||
ml_doc: 0 as *const $crate::_detail::libc::c_char
|
||||
};
|
||||
let py: Python = $py;
|
||||
unsafe {
|
||||
let obj = $crate::_detail::ffi::PyCFunction_New(&mut method_def, ::std::ptr::null_mut());
|
||||
$crate::_detail::from_owned_ptr_or_panic(py, obj)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[test]
|
||||
fn it_works() {
|
||||
let gil = Python::acquire_gil();
|
||||
|
|
|
@ -39,7 +39,7 @@ macro_rules! exc_type(
|
|||
|
||||
impl <'p> PythonObjectWithTypeObject<'p> for $name<'p> {
|
||||
#[inline]
|
||||
fn type_object(py: Python<'p>, _ : Option<&Self>) -> PyType<'p> {
|
||||
fn type_object(py: Python<'p>) -> PyType<'p> {
|
||||
unsafe { PyType::from_type_ptr(py, ffi::$exc_name as *mut ffi::PyTypeObject) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ macro_rules! pyobject_newtype(
|
|||
|
||||
impl <'p> ::python::PythonObjectWithTypeObject<'p> for $name<'p> {
|
||||
#[inline]
|
||||
fn type_object(py: ::python::Python<'p>, _ : Option<&Self>) -> ::objects::typeobject::PyType<'p> {
|
||||
fn type_object(py: ::python::Python<'p>) -> ::objects::typeobject::PyType<'p> {
|
||||
unsafe { ::objects::typeobject::PyType::from_type_ptr(py, &mut ::ffi::$typeobject) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ impl <'p> PythonObjectWithCheckedDowncast<'p> for PyObject<'p> {
|
|||
|
||||
impl <'p> PythonObjectWithTypeObject<'p> for PyObject<'p> {
|
||||
#[inline]
|
||||
fn type_object(py: Python<'p>, _ : Option<&Self>) -> PyType<'p> {
|
||||
fn type_object(py: Python<'p>) -> PyType<'p> {
|
||||
unsafe { PyType::from_type_ptr(py, &mut ffi::PyBaseObject_Type) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -65,6 +65,16 @@ impl<'p> std::ops::Index<usize> for PyTuple<'p> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'p, 'a> std::iter::IntoIterator for &'a PyTuple<'p> {
|
||||
type Item = &'a PyObject<'p>;
|
||||
type IntoIter = std::slice::Iter<'a, PyObject<'p>>;
|
||||
|
||||
#[inline]
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn wrong_tuple_length<'p>(t: &PyTuple<'p>, expected_length: usize) -> PyErr<'p> {
|
||||
let py = t.python();
|
||||
|
|
|
@ -68,8 +68,7 @@ pub trait PythonObjectWithCheckedDowncast<'p> : PythonObject<'p> {
|
|||
/// Trait implemented by python object types that have a corresponding type object.
|
||||
pub trait PythonObjectWithTypeObject<'p> : PythonObjectWithCheckedDowncast<'p> {
|
||||
/// Retrieves the type object for this python object type.
|
||||
#[unstable = "Option<&Self> will disappear when UFCS is implemented"]
|
||||
fn type_object(Python<'p>, Option<&Self>) -> PyType<'p>;
|
||||
fn type_object(Python<'p>) -> PyType<'p>;
|
||||
}
|
||||
|
||||
/// ToPythonPointer for borrowed python pointers.
|
||||
|
@ -157,8 +156,7 @@ impl<'p> Python<'p> {
|
|||
/// Retrieves a reference to the type object for type T.
|
||||
#[inline]
|
||||
pub fn get_type<T>(self) -> PyType<'p> where T: PythonObjectWithTypeObject<'p> {
|
||||
let none : Option<&T> = None;
|
||||
PythonObjectWithTypeObject::type_object(self, none)
|
||||
T::type_object(self)
|
||||
}
|
||||
|
||||
/// Import the python module with the specified name.
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
#![crate_type = "dylib"]
|
||||
|
||||
#[macro_use] extern crate cpython;
|
||||
|
||||
use cpython::{PyModule, PyResult, Python};
|
||||
|
||||
py_module_initializer!("testmodule", inittestmodule, |py, m| {
|
||||
println!("in initializer");
|
||||
try!(m.add(cstr!("__doc__"), "Module documentation string"));
|
||||
try!(m.add(cstr!("__author__"), "Daniel Grunwald"));
|
||||
try!(m.add(cstr!("__version__"), "0.0.1"));
|
||||
Ok(())
|
||||
});
|
||||
|
Loading…
Reference in New Issue