Add support for exposing rust functions to python
This commit is contained in:
parent
60360fa03f
commit
f8e24a870c
|
@ -1,4 +1,3 @@
|
||||||
/target
|
/target
|
||||||
/Cargo.lock
|
/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.
|
/// Construct PyObject from the result of a python FFI call that returns a borrowed reference.
|
||||||
/// Returns Err(PyErr) if the pointer is null.
|
/// Returns Err(PyErr) if the pointer is null.
|
||||||
/// Unsafe because the pointer might be invalid.
|
/// 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 err::{PyErr, PyResult};
|
||||||
pub use objects::*;
|
pub use objects::*;
|
||||||
pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject};
|
pub use python::{Python, PythonObject, PythonObjectWithCheckedDowncast, PythonObjectWithTypeObject};
|
||||||
|
pub use pythonrun::{GILGuard, prepare_freethreaded_python};
|
||||||
pub use conversion::{FromPyObject, ToPyObject};
|
pub use conversion::{FromPyObject, ToPyObject};
|
||||||
pub use objectprotocol::{ObjectProtocol};
|
pub use objectprotocol::{ObjectProtocol};
|
||||||
pub use std::ffi::CStr;
|
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! cstr(
|
macro_rules! cstr(
|
||||||
|
@ -33,6 +33,14 @@ mod objects;
|
||||||
mod objectprotocol;
|
mod objectprotocol;
|
||||||
mod pythonrun;
|
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_export]
|
||||||
macro_rules! py_module_initializer {
|
macro_rules! py_module_initializer {
|
||||||
($name: tt, $init_funcname: ident, $init: expr) => {
|
($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]
|
#[test]
|
||||||
fn it_works() {
|
fn it_works() {
|
||||||
let gil = Python::acquire_gil();
|
let gil = Python::acquire_gil();
|
||||||
|
|
|
@ -39,7 +39,7 @@ macro_rules! exc_type(
|
||||||
|
|
||||||
impl <'p> PythonObjectWithTypeObject<'p> for $name<'p> {
|
impl <'p> PythonObjectWithTypeObject<'p> for $name<'p> {
|
||||||
#[inline]
|
#[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) }
|
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> {
|
impl <'p> ::python::PythonObjectWithTypeObject<'p> for $name<'p> {
|
||||||
#[inline]
|
#[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) }
|
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> {
|
impl <'p> PythonObjectWithTypeObject<'p> for PyObject<'p> {
|
||||||
#[inline]
|
#[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) }
|
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> {
|
fn wrong_tuple_length<'p>(t: &PyTuple<'p>, expected_length: usize) -> PyErr<'p> {
|
||||||
let py = t.python();
|
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.
|
/// Trait implemented by python object types that have a corresponding type object.
|
||||||
pub trait PythonObjectWithTypeObject<'p> : PythonObjectWithCheckedDowncast<'p> {
|
pub trait PythonObjectWithTypeObject<'p> : PythonObjectWithCheckedDowncast<'p> {
|
||||||
/// Retrieves the type object for this python object type.
|
/// Retrieves the type object for this python object type.
|
||||||
#[unstable = "Option<&Self> will disappear when UFCS is implemented"]
|
fn type_object(Python<'p>) -> PyType<'p>;
|
||||||
fn type_object(Python<'p>, Option<&Self>) -> PyType<'p>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ToPythonPointer for borrowed python pointers.
|
/// ToPythonPointer for borrowed python pointers.
|
||||||
|
@ -157,8 +156,7 @@ impl<'p> Python<'p> {
|
||||||
/// Retrieves a reference to the type object for type T.
|
/// Retrieves a reference to the type object for type T.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn get_type<T>(self) -> PyType<'p> where T: PythonObjectWithTypeObject<'p> {
|
pub fn get_type<T>(self) -> PyType<'p> where T: PythonObjectWithTypeObject<'p> {
|
||||||
let none : Option<&T> = None;
|
T::type_object(self)
|
||||||
PythonObjectWithTypeObject::type_object(self, none)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Import the python module with the specified name.
|
/// 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