Skip to content

Commit 5414cfb

Browse files
committed
Add py_class_method!()
1 parent 4980053 commit 5414cfb

File tree

4 files changed

+117
-7
lines changed

4 files changed

+117
-7
lines changed

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ pub mod _detail {
126126
pub use err::from_owned_ptr_or_panic;
127127
pub use function::py_fn_impl;
128128
#[cfg(feature="python27-sys")]
129-
pub use rustobject::method::py_method_impl;
129+
pub use rustobject::method::{py_method_impl, py_class_method_impl};
130130

131131
/// assume_gil_acquired(), but the returned Python<'p> is bounded by the scope
132132
/// of the referenced variable.

src/objects/mod.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,9 @@ pub use self::num::{PyLong, PyFloat};
4141

4242
macro_rules! pyobject_newtype(
4343
($name: ident) => (
44-
#[repr(C)]
4544
#[derive(Clone)]
4645
pub struct $name<'p>(::objects::object::PyObject<'p>);
47-
46+
4847
impl <'p> ::python::ToPythonPointer for $name<'p> {
4948
#[inline]
5049
fn as_ptr(&self) -> *mut ::ffi::PyObject {
@@ -56,7 +55,7 @@ macro_rules! pyobject_newtype(
5655
::python::ToPythonPointer::steal_ptr(self.0)
5756
}
5857
}
59-
58+
6059
impl <'p> ::python::PythonObject<'p> for $name<'p> {
6160
#[inline]
6261
fn as_object(&self) -> &::objects::object::PyObject<'p> {

src/rustobject/method.rs

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,18 @@ use std::{ptr, marker};
2020
use python::{Python, PythonObject};
2121
use objects::{PyObject, PyTuple, PyType};
2222
use conversion::ToPyObject;
23+
use super::{PythonBaseObject, PyRustObject, PyRustType};
2324
use super::typebuilder::TypeMember;
2425
use ffi;
2526
use err;
2627

2728
/// Creates a python instance method descriptor that invokes a Rust function.
2829
///
2930
/// As arguments, takes the name of a rust function with the signature
30-
/// `for<'p> fn(&PyRustObject<'p, _>, &PyTuple<'p>) -> PyResult<'p, T>`
31-
/// for some `T` that implements `ToPyObject`.
31+
/// `for<'p> fn(&PyRustObject<'p, T>, &PyTuple<'p>) -> PyResult<'p, R>`
32+
/// for some `R` that implements `ToPyObject`.
3233
///
33-
/// Returns a type that implements `pythonobject::TypeMember<PyRustObject<_>>`
34+
/// Returns a type that implements `typebuilder::TypeMember<PyRustObject<T>>`
3435
/// by producing an instance method descriptor.
3536
///
3637
/// # Example
@@ -117,3 +118,93 @@ impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p>
117118
}
118119
}
119120

121+
122+
/// Creates a python class method descriptor that invokes a Rust function.
123+
///
124+
/// As arguments, takes the name of a rust function with the signature
125+
/// `for<'p> fn(&PyType<'p>, &PyTuple<'p>) -> PyResult<'p, T>`
126+
/// for some `T` that implements `ToPyObject`.
127+
///
128+
/// Returns a type that implements `typebuilder::TypeMember<PyRustObject<_>>`
129+
/// by producing an class method descriptor.
130+
///
131+
/// # Example
132+
/// ```
133+
/// #![feature(plugin)]
134+
/// #![plugin(interpolate_idents)]
135+
/// #[macro_use] extern crate cpython;
136+
/// use cpython::{Python, PythonObject, PyResult, PyErr, ObjectProtocol,
137+
/// PyTuple, PyType, PyRustTypeBuilder, NoArgs};
138+
/// use cpython::{exc};
139+
///
140+
/// fn method<'p>(ty: &PyType<'p>, args: &PyTuple<'p>) -> PyResult<'p, i32> {
141+
/// Ok(42)
142+
/// }
143+
///
144+
/// fn main() {
145+
/// let gil = Python::acquire_gil();
146+
/// let my_type = PyRustTypeBuilder::<i32>::new(gil.python(), "MyType")
147+
/// .add("method", py_class_method!(method))
148+
/// .finish().unwrap();
149+
/// let result = my_type.as_object().call_method("method", &NoArgs, None).unwrap();
150+
/// assert_eq!(42, result.extract::<i32>().unwrap());
151+
/// }
152+
/// ```
153+
#[macro_export]
154+
macro_rules! py_class_method {
155+
($f: ident) => ( interpolate_idents! {{
156+
unsafe extern "C" fn [ wrap_ $f ](
157+
slf: *mut $crate::_detail::ffi::PyObject,
158+
args: *mut $crate::_detail::ffi::PyObject)
159+
-> *mut $crate::_detail::ffi::PyObject
160+
{
161+
let _guard = $crate::_detail::PanicGuard::with_message("Rust panic in py_method!");
162+
let py = $crate::_detail::bounded_assume_gil_acquired(&args);
163+
let slf = $crate::PyObject::from_borrowed_ptr(py, slf);
164+
let slf = <$crate::PyType as $crate::PythonObject>::unchecked_downcast_from(slf);
165+
let args = $crate::PyObject::from_borrowed_ptr(py, args);
166+
let args = <$crate::PyTuple as $crate::PythonObject>::unchecked_downcast_from(args);
167+
match $f(&slf, &args) {
168+
Ok(val) => {
169+
let obj = $crate::ToPyObject::into_py_object(val, py);
170+
return $crate::ToPythonPointer::steal_ptr(obj);
171+
}
172+
Err(e) => {
173+
e.restore();
174+
return ::std::ptr::null_mut();
175+
}
176+
}
177+
}
178+
static mut [ method_def_ $f ]: $crate::_detail::ffi::PyMethodDef = $crate::_detail::ffi::PyMethodDef {
179+
//ml_name: bytes!(stringify!($f), "\0"),
180+
ml_name: b"<rust method>\0" as *const u8 as *const $crate::_detail::libc::c_char,
181+
ml_meth: Some([ wrap_ $f ]),
182+
ml_flags: $crate::_detail::ffi::METH_VARARGS | $crate::_detail::ffi::METH_CLASS,
183+
ml_doc: 0 as *const $crate::_detail::libc::c_char
184+
};
185+
unsafe { $crate::_detail::py_class_method_impl(&mut [ method_def_ $f ], $f) }
186+
}})
187+
}
188+
189+
pub struct ClassMethodDescriptor(*mut ffi::PyMethodDef);
190+
191+
// py_method_impl takes fn(&T) to ensure that the T in MethodDescriptor<T>
192+
// corresponds to the T in the function signature.
193+
pub unsafe fn py_class_method_impl<'p, R>(
194+
def: *mut ffi::PyMethodDef,
195+
_f: fn(&PyType<'p>, &PyTuple<'p>) -> err::PyResult<'p, R>
196+
) -> ClassMethodDescriptor
197+
{
198+
ClassMethodDescriptor(def)
199+
}
200+
201+
impl <'p, T> TypeMember<'p, T> for ClassMethodDescriptor where T: PythonObject<'p> {
202+
#[inline]
203+
fn into_descriptor(self, ty: &PyType<'p>, name: &str) -> PyObject<'p> {
204+
unsafe {
205+
err::from_owned_ptr_or_panic(ty.python(),
206+
ffi::PyDescr_NewClassMethod(ty.as_type_ptr(), self.0))
207+
}
208+
}
209+
}
210+

src/rustobject/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,26 @@ impl <'p, T, B> ToPythonPointer for PyRustObject<'p, T, B> where T: 'static + Se
154154
}
155155
}
156156

157+
impl <'p, 's, T, B> ToPyObject<'p> for PyRustObject<'s, T, B> where T: 'static + Send, B: PythonBaseObject<'s> {
158+
type ObjectType = PyObject<'p>;
159+
160+
#[inline]
161+
fn to_py_object(&self, py: Python<'p>) -> PyObject<'p> {
162+
self.as_object().to_py_object(py)
163+
}
164+
165+
#[inline]
166+
fn into_py_object(self, py: Python<'p>) -> PyObject<'p> {
167+
self.into_object().into_py_object(py)
168+
}
169+
170+
#[inline]
171+
fn with_borrowed_ptr<F, R>(&self, py: Python<'p>, f: F) -> R
172+
where F: FnOnce(*mut ffi::PyObject) -> R {
173+
f(self.as_ptr())
174+
}
175+
}
176+
157177
impl <'p, T, B> PythonObject<'p> for PyRustObject<'p, T, B> where T: 'static + Send, B: PythonBaseObject<'p> {
158178
#[inline]
159179
fn as_object(&self) -> &PyObject<'p> {

0 commit comments

Comments
 (0)