@@ -20,17 +20,18 @@ use std::{ptr, marker};
20
20
use python:: { Python , PythonObject } ;
21
21
use objects:: { PyObject , PyTuple , PyType } ;
22
22
use conversion:: ToPyObject ;
23
+ use super :: { PythonBaseObject , PyRustObject , PyRustType } ;
23
24
use super :: typebuilder:: TypeMember ;
24
25
use ffi;
25
26
use err;
26
27
27
28
/// Creates a python instance method descriptor that invokes a Rust function.
28
29
///
29
30
/// 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`.
32
33
///
33
- /// Returns a type that implements `pythonobject ::TypeMember<PyRustObject<_ >>`
34
+ /// Returns a type that implements `typebuilder ::TypeMember<PyRustObject<T >>`
34
35
/// by producing an instance method descriptor.
35
36
///
36
37
/// # Example
@@ -117,3 +118,93 @@ impl <'p, T> TypeMember<'p, T> for MethodDescriptor<T> where T: PythonObject<'p>
117
118
}
118
119
}
119
120
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
+
0 commit comments