Skip to content

Commit

Permalink
Add py_class_method!()
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed Jun 26, 2015
1 parent 4980053 commit 5414cfb
Show file tree
Hide file tree
Showing 4 changed files with 117 additions and 7 deletions.
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pub mod _detail {
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;
pub use rustobject::method::{py_method_impl, py_class_method_impl};

/// assume_gil_acquired(), but the returned Python<'p> is bounded by the scope
/// of the referenced variable.
Expand Down
5 changes: 2 additions & 3 deletions src/objects/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,9 @@ pub use self::num::{PyLong, PyFloat};

macro_rules! pyobject_newtype(
($name: ident) => (
#[repr(C)]
#[derive(Clone)]
pub struct $name<'p>(::objects::object::PyObject<'p>);

impl <'p> ::python::ToPythonPointer for $name<'p> {
#[inline]
fn as_ptr(&self) -> *mut ::ffi::PyObject {
Expand All @@ -56,7 +55,7 @@ macro_rules! pyobject_newtype(
::python::ToPythonPointer::steal_ptr(self.0)
}
}

impl <'p> ::python::PythonObject<'p> for $name<'p> {
#[inline]
fn as_object(&self) -> &::objects::object::PyObject<'p> {
Expand Down
97 changes: 94 additions & 3 deletions src/rustobject/method.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,18 @@ use std::{ptr, marker};
use python::{Python, PythonObject};
use objects::{PyObject, PyTuple, PyType};
use conversion::ToPyObject;
use super::{PythonBaseObject, PyRustObject, PyRustType};
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`.
/// `for<'p> fn(&PyRustObject<'p, T>, &PyTuple<'p>) -> PyResult<'p, R>`
/// for some `R` that implements `ToPyObject`.
///
/// Returns a type that implements `pythonobject::TypeMember<PyRustObject<_>>`
/// Returns a type that implements `typebuilder::TypeMember<PyRustObject<T>>`
/// by producing an instance method descriptor.
///
/// # Example
Expand Down Expand Up @@ -117,3 +118,93 @@ impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p>
}
}


/// Creates a python class method descriptor that invokes a Rust function.
///
/// As arguments, takes the name of a rust function with the signature
/// `for<'p> fn(&PyType<'p>, &PyTuple<'p>) -> PyResult<'p, T>`
/// for some `T` that implements `ToPyObject`.
///
/// Returns a type that implements `typebuilder::TypeMember<PyRustObject<_>>`
/// by producing an class method descriptor.
///
/// # Example
/// ```
/// #![feature(plugin)]
/// #![plugin(interpolate_idents)]
/// #[macro_use] extern crate cpython;
/// use cpython::{Python, PythonObject, PyResult, PyErr, ObjectProtocol,
/// PyTuple, PyType, PyRustTypeBuilder, NoArgs};
/// use cpython::{exc};
///
/// fn method<'p>(ty: &PyType<'p>, args: &PyTuple<'p>) -> PyResult<'p, i32> {
/// Ok(42)
/// }
///
/// fn main() {
/// let gil = Python::acquire_gil();
/// let my_type = PyRustTypeBuilder::<i32>::new(gil.python(), "MyType")
/// .add("method", py_class_method!(method))
/// .finish().unwrap();
/// let result = my_type.as_object().call_method("method", &NoArgs, None).unwrap();
/// assert_eq!(42, result.extract::<i32>().unwrap());
/// }
/// ```
#[macro_export]
macro_rules! py_class_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::PyType as $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 | $crate::_detail::ffi::METH_CLASS,
ml_doc: 0 as *const $crate::_detail::libc::c_char
};
unsafe { $crate::_detail::py_class_method_impl(&mut [ method_def_ $f ], $f) }
}})
}

pub struct ClassMethodDescriptor(*mut ffi::PyMethodDef);

// 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_class_method_impl<'p, R>(
def: *mut ffi::PyMethodDef,
_f: fn(&PyType<'p>, &PyTuple<'p>) -> err::PyResult<'p, R>
) -> ClassMethodDescriptor
{
ClassMethodDescriptor(def)
}

impl <'p, T> TypeMember<'p, T> for ClassMethodDescriptor 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_NewClassMethod(ty.as_type_ptr(), self.0))
}
}
}

20 changes: 20 additions & 0 deletions src/rustobject/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,26 @@ impl <'p, T, B> ToPythonPointer for PyRustObject<'p, T, B> where T: 'static + Se
}
}

impl <'p, 's, T, B> ToPyObject<'p> for PyRustObject<'s, T, B> where T: 'static + Send, B: PythonBaseObject<'s> {
type ObjectType = PyObject<'p>;

#[inline]
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
self.as_object().to_py_object(py)
}

#[inline]
fn into_py_object(self, py: Python<'p>) -> PyObject<'p> {
self.into_object().into_py_object(py)
}

#[inline]
fn with_borrowed_ptr<F, R>(&self, py: Python<'p>, f: F) -> R
where F: FnOnce(*mut ffi::PyObject) -> R {
f(self.as_ptr())
}
}

impl <'p, T, B> PythonObject<'p> for PyRustObject<'p, T, B> where T: 'static + Send, B: PythonBaseObject<'p> {
#[inline]
fn as_object(&self) -> &PyObject<'p> {
Expand Down

0 comments on commit 5414cfb

Please sign in to comment.