Add py_method!() macro for creating method descriptors.
This commit is contained in:
parent
5c74f55862
commit
4980053e95
|
@ -33,6 +33,8 @@ stamps/test-hello: hello.out
|
|||
@grep "Rust says: Hello Python!" hello.out >/dev/null
|
||||
@grep "Rust got 42" hello.out >/dev/null
|
||||
|
||||
all: custom_type.so
|
||||
|
||||
all: inheritance.so
|
||||
|
||||
|
||||
|
|
21
extensions/custom_type.rs
Normal file
21
extensions/custom_type.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
#![crate_type = "dylib"]
|
||||
#![feature(plugin)]
|
||||
#![plugin(interpolate_idents)]
|
||||
|
||||
#[macro_use] extern crate cpython;
|
||||
|
||||
use cpython::{PythonObject, PyObject, PyRustObject, PyTuple, PyResult};
|
||||
|
||||
py_module_initializer!(custom_type, |_py, m| {
|
||||
try!(m.add("__doc__", "Module documentation string"));
|
||||
try!(m.add_type::<i32>("MyType")
|
||||
.add("a", py_method!(a))
|
||||
.finish());
|
||||
Ok(())
|
||||
});
|
||||
|
||||
fn a<'p>(slf: &PyRustObject<'p, i32>, args: &PyTuple<'p>) -> PyResult<'p, PyObject<'p>> {
|
||||
println!("a() was called with self={:?} and args={:?}", slf.get(), args.as_object());
|
||||
Ok(slf.python().None())
|
||||
}
|
||||
|
87
src/function.rs
Normal file
87
src/function.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
// Copyright (c) 2015 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use libc::c_char;
|
||||
use std::ptr;
|
||||
use python::Python;
|
||||
use objects::PyObject;
|
||||
use conversion::ToPyObject;
|
||||
use ffi;
|
||||
use err;
|
||||
|
||||
/// Creates a python callable object that invokes a Rust function.
|
||||
///
|
||||
/// As arguments, takes the name of a rust function with the signature
|
||||
/// `for<'p> fn(Python<'p>, &PyTuple<'p>) -> PyResult<'p, T>`
|
||||
/// for some `T` that implements `ToPyObject`.
|
||||
///
|
||||
/// Returns a type that implements `ToPyObject` by producing a python callable.
|
||||
///
|
||||
/// See `py_module_initializer!` for example usage.
|
||||
#[macro_export]
|
||||
macro_rules! py_fn {
|
||||
($f: ident) => ( interpolate_idents! {{
|
||||
unsafe extern "C" fn [ wrap_ $f ](
|
||||
_slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_fn!");
|
||||
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
|
||||
let args = $crate::PyObject::from_borrowed_ptr(py, args);
|
||||
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
|
||||
match $f(py, &args) {
|
||||
Ok(val) => {
|
||||
let obj = $crate::ToPyObject::into_py_object(val, py);
|
||||
return $crate::ToPythonPointer::steal_ptr(obj);
|
||||
}
|
||||
Err(e) => {
|
||||
e.restore();
|
||||
return ::std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
|
||||
//ml_name: bytes!(stringify!($f), "\0"),
|
||||
ml_name: 0 as *const $crate::_detail::libc::c_char,
|
||||
ml_meth: Some([ wrap_ $f ]),
|
||||
ml_flags: $crate::_detail::ffi::METH_VARARGS,
|
||||
ml_doc: 0 as *const $crate::_detail::libc::c_char
|
||||
};
|
||||
unsafe {
|
||||
$crate::_detail::py_fn_impl(&mut [ method_def_ $f ])
|
||||
}
|
||||
}})
|
||||
}
|
||||
|
||||
pub struct PyFn(*mut ffi::PyMethodDef);
|
||||
|
||||
pub unsafe fn py_fn_impl(def: *mut ffi::PyMethodDef) -> PyFn {
|
||||
PyFn(def)
|
||||
}
|
||||
|
||||
impl <'p> ToPyObject<'p> for PyFn {
|
||||
type ObjectType = PyObject<'p>;
|
||||
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
|
||||
unsafe {
|
||||
err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(self.0, ptr::null_mut()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
67
src/lib.rs
67
src/lib.rs
|
@ -113,6 +113,7 @@ mod conversion;
|
|||
mod objects;
|
||||
mod objectprotocol;
|
||||
mod pythonrun;
|
||||
mod function;
|
||||
#[cfg(feature="python27-sys")]
|
||||
mod rustobject;
|
||||
|
||||
|
@ -123,6 +124,9 @@ pub mod _detail {
|
|||
pub use libc;
|
||||
pub use abort_on_panic::PanicGuard;
|
||||
pub use err::from_owned_ptr_or_panic;
|
||||
pub use function::py_fn_impl;
|
||||
#[cfg(feature="python27-sys")]
|
||||
pub use rustobject::method::py_method_impl;
|
||||
|
||||
/// assume_gil_acquired(), but the returned Python<'p> is bounded by the scope
|
||||
/// of the referenced variable.
|
||||
|
@ -282,66 +286,3 @@ pub unsafe fn py_module_initializer_impl(
|
|||
})
|
||||
}
|
||||
|
||||
/// Creates a python callable object that invokes a Rust function.
|
||||
///
|
||||
/// As arguments, takes the name of a rust function with the signature
|
||||
/// `<'p>(Python<'p>, &PyTuple<'p>) -> PyResult<'p, T>`
|
||||
/// for some `T` that implements `ToPyObject`.
|
||||
///
|
||||
/// Returns a type that implements `ToPyObject` by producing a python callable.
|
||||
///
|
||||
/// See `py_module_initializer!` for example usage.
|
||||
#[macro_export]
|
||||
macro_rules! py_fn {
|
||||
($f: ident) => ( interpolate_idents! {{
|
||||
unsafe extern "C" fn [ wrap_ $f ](
|
||||
_slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_fn!");
|
||||
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
|
||||
let args = $crate::PyObject::from_borrowed_ptr(py, args);
|
||||
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
|
||||
match $f(py, &args) {
|
||||
Ok(val) => {
|
||||
let obj = $crate::ToPyObject::into_py_object(val, py);
|
||||
return $crate::ToPythonPointer::steal_ptr(obj);
|
||||
}
|
||||
Err(e) => {
|
||||
e.restore();
|
||||
return ::std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
static mut [ method_def_ $f ]: $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_ $f ]),
|
||||
ml_flags: $crate::_detail::ffi::METH_VARARGS,
|
||||
ml_doc: 0 as *const $crate::_detail::libc::c_char
|
||||
};
|
||||
unsafe { $crate::PyRustFunctionHandle::new(&mut [ method_def_ $f ]) }
|
||||
}})
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
pub struct PyRustFunctionHandle(*mut ffi::PyMethodDef);
|
||||
|
||||
impl PyRustFunctionHandle {
|
||||
#[inline]
|
||||
pub unsafe fn new(p: *mut ffi::PyMethodDef) -> PyRustFunctionHandle {
|
||||
PyRustFunctionHandle(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl <'p> ToPyObject<'p> for PyRustFunctionHandle {
|
||||
type ObjectType = PyObject<'p>;
|
||||
|
||||
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
|
||||
unsafe {
|
||||
err::from_owned_ptr_or_panic(py, ffi::PyCFunction_New(self.0, ptr::null_mut()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,2 +1,119 @@
|
|||
use python::Python;
|
||||
// Copyright (c) 2015 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::{ptr, marker};
|
||||
use python::{Python, PythonObject};
|
||||
use objects::{PyObject, PyTuple, PyType};
|
||||
use conversion::ToPyObject;
|
||||
use super::typebuilder::TypeMember;
|
||||
use ffi;
|
||||
use err;
|
||||
|
||||
/// Creates a python instance method descriptor that invokes a Rust function.
|
||||
///
|
||||
/// As arguments, takes the name of a rust function with the signature
|
||||
/// `for<'p> fn(&PyRustObject<'p, _>, &PyTuple<'p>) -> PyResult<'p, T>`
|
||||
/// for some `T` that implements `ToPyObject`.
|
||||
///
|
||||
/// Returns a type that implements `pythonobject::TypeMember<PyRustObject<_>>`
|
||||
/// by producing an instance method descriptor.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// #![feature(plugin)]
|
||||
/// #![plugin(interpolate_idents)]
|
||||
/// #[macro_use] extern crate cpython;
|
||||
/// use cpython::{Python, PythonObject, PyResult, PyErr, ObjectProtocol,
|
||||
/// PyTuple, PyRustObject, PyRustTypeBuilder};
|
||||
/// use cpython::{exc};
|
||||
///
|
||||
/// fn mul<'p>(slf: &PyRustObject<'p, i32>, args: &PyTuple<'p>) -> PyResult<'p, i32> {
|
||||
/// let py = slf.python();
|
||||
/// match slf.get().checked_mul(try!(args.get_item(0).extract::<i32>())) {
|
||||
/// Some(val) => Ok(val),
|
||||
/// None => Err(PyErr::new_lazy_init(py.get_type::<exc::OverflowError>(), None))
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn main() {
|
||||
/// let gil = Python::acquire_gil();
|
||||
/// let multiplier_type = PyRustTypeBuilder::<i32>::new(gil.python(), "Multiplier")
|
||||
/// .add("mul", py_method!(mul))
|
||||
/// .finish().unwrap();
|
||||
/// let obj = multiplier_type.create_instance(3, ()).into_object();
|
||||
/// let result = obj.call_method("mul", &(4,), None).unwrap().extract::<i32>().unwrap();
|
||||
/// assert_eq!(result, 12);
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! py_method {
|
||||
($f: ident) => ( interpolate_idents! {{
|
||||
unsafe extern "C" fn [ wrap_ $f ](
|
||||
slf: *mut $crate::_detail::ffi::PyObject,
|
||||
args: *mut $crate::_detail::ffi::PyObject)
|
||||
-> *mut $crate::_detail::ffi::PyObject
|
||||
{
|
||||
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_method!");
|
||||
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
|
||||
let slf = $crate::PyObject::from_borrowed_ptr(py, slf);
|
||||
let slf = $crate::PythonObject::unchecked_downcast_from(slf);
|
||||
let args = $crate::PyObject::from_borrowed_ptr(py, args);
|
||||
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
|
||||
match $f(&slf, &args) {
|
||||
Ok(val) => {
|
||||
let obj = $crate::ToPyObject::into_py_object(val, py);
|
||||
return $crate::ToPythonPointer::steal_ptr(obj);
|
||||
}
|
||||
Err(e) => {
|
||||
e.restore();
|
||||
return ::std::ptr::null_mut();
|
||||
}
|
||||
}
|
||||
}
|
||||
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
|
||||
//ml_name: bytes!(stringify!($f), "\0"),
|
||||
ml_name: b"<rust method>\0" as *const u8 as *const $crate::_detail::libc::c_char,
|
||||
ml_meth: Some([ wrap_ $f ]),
|
||||
ml_flags: $crate::_detail::ffi::METH_VARARGS,
|
||||
ml_doc: 0 as *const $crate::_detail::libc::c_char
|
||||
};
|
||||
unsafe { $crate::_detail::py_method_impl(&mut [ method_def_ $f ], $f) }
|
||||
}})
|
||||
}
|
||||
|
||||
pub struct MethodDescriptor<T>(*mut ffi::PyMethodDef, marker::PhantomData<fn(&T)>);
|
||||
|
||||
// py_method_impl takes fn(&T) to ensure that the T in MethodDescriptor<T>
|
||||
// corresponds to the T in the function signature.
|
||||
pub unsafe fn py_method_impl<'p, T, R>(
|
||||
def: *mut ffi::PyMethodDef,
|
||||
_f: fn(&T, &PyTuple<'p>) -> err::PyResult<'p, R>
|
||||
) -> MethodDescriptor<T> {
|
||||
MethodDescriptor(def, marker::PhantomData)
|
||||
}
|
||||
|
||||
impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p> {
|
||||
#[inline]
|
||||
fn into_descriptor(self, ty: &PyType<'p>, name: &str) -> PyObject<'p> {
|
||||
unsafe {
|
||||
err::from_owned_ptr_or_panic(ty.python(),
|
||||
ffi::PyDescr_NewMethod(ty.as_type_ptr(), self.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -25,7 +25,9 @@ use std::{mem, ops, ptr, marker};
|
|||
use err::{self, PyResult};
|
||||
|
||||
pub mod typebuilder;
|
||||
mod method;
|
||||
pub mod method;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
/// A PythonObject that is usable as a base type with PyTypeBuilder::base().
|
||||
pub trait PythonBaseObject<'p> : PythonObject<'p> {
|
||||
|
|
|
@ -1,8 +1,24 @@
|
|||
extern crate cpython;
|
||||
// Copyright (c) 2015 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use cpython::*;
|
||||
use {Python, NoArgs, PythonObject, PyRustTypeBuilder};
|
||||
|
||||
#[test]
|
||||
fn rustobject_calls_drop() {
|
||||
|
@ -29,7 +45,7 @@ fn rustobject_calls_drop() {
|
|||
|
||||
|
||||
#[test]
|
||||
fn rustobject_no_init_from_python() {
|
||||
fn no_init_from_python() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let t = PyRustTypeBuilder::<i32>::new(py, "MyType").finish().unwrap();
|
||||
|
@ -38,7 +54,7 @@ fn rustobject_no_init_from_python() {
|
|||
|
||||
|
||||
#[test]
|
||||
fn rustobject_heaptype_refcount() {
|
||||
fn heaptype_refcount() {
|
||||
let gil = Python::acquire_gil();
|
||||
let py = gil.python();
|
||||
let t = PyRustTypeBuilder::<i32>::new(py, "MyType").finish().unwrap();
|
|
@ -1,3 +1,21 @@
|
|||
// Copyright (c) 2015 Daniel Grunwald
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of this
|
||||
// software and associated documentation files (the "Software"), to deal in the Software
|
||||
// without restriction, including without limitation the rights to use, copy, modify, merge,
|
||||
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
|
||||
// to whom the Software is furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all copies or
|
||||
// substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
||||
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
// DEALINGS IN THE SOFTWARE.
|
||||
|
||||
use libc;
|
||||
use ffi;
|
||||
use python::{Python, ToPythonPointer, PythonObject};
|
||||
|
|
Loading…
Reference in a new issue