cpython/py_class/
members.rs

1// Copyright (c) 2016 Daniel Grunwald
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy of this
4// software and associated documentation files (the "Software"), to deal in the Software
5// without restriction, including without limitation the rights to use, copy, modify, merge,
6// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
7// to whom the Software is furnished to do so, subject to the following conditions:
8//
9// The above copyright notice and this permission notice shall be included in all copies or
10// substantial portions of the Software.
11//
12// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
13// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
15// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
16// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
17// DEALINGS IN THE SOFTWARE.
18
19use std::marker;
20
21use crate::conversion::ToPyObject;
22use crate::err::{self, PyResult};
23use crate::ffi;
24use crate::objects::PyObject;
25use crate::python::{Python, PythonObject};
26
27/// Represents something that can be added as a member to a Python class/type.
28///
29/// T: type of rust class used for instances of the Python class/type.
30pub trait TypeMember<T>
31where
32    T: PythonObject,
33{
34    /// Convert the type member into a python object
35    /// that can be stored in the type dict.
36    ///
37    /// Because the member may expect `self` values to be of type `T`,
38    /// `ty` must be T::type_object() or a derived class.
39    /// (otherwise the behavior is undefined)
40    unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject>;
41}
42
43impl<T, S> TypeMember<T> for S
44where
45    T: PythonObject,
46    S: ToPyObject,
47{
48    #[inline]
49    unsafe fn into_descriptor(self, py: Python, _ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
50        Ok(self.into_py_object(py).into_object())
51    }
52}
53
54#[macro_export]
55#[doc(hidden)]
56macro_rules! py_class_init_members {
57    ($class:ident, $py:ident, $type_object: ident, { }) => {{}};
58    ($class:ident, $py:ident, $type_object: ident, { $( $name:ident = $init:expr; )+ }) => {{
59        let dict = $crate::PyDict::new($py);
60        $( {
61            // keep $init out of unsafe block; it might contain user code
62            let init = $init;
63            let descriptor = unsafe {
64                $crate::py_class::members::TypeMember::<$class>::into_descriptor(init, $py, &mut $type_object)
65            }?;
66            let name = $crate::strip_raw!(stringify!($name));
67            dict.set_item($py, name, descriptor)?;
68        })*
69        unsafe {
70            assert!($type_object.tp_dict.is_null());
71            $type_object.tp_dict = $crate::PythonObject::into_object(dict).steal_ptr();
72        }
73    }};
74}
75
76#[macro_export]
77#[doc(hidden)]
78macro_rules! py_class_instance_method {
79    ($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
80        $crate::py_class_instance_method!($py, $class::$f, { "" } [ $( { $pname : $ptype = $detail } )* ])
81    }};
82
83    ($py:ident, $class:ident :: $f:ident, { $doc:expr } [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
84        unsafe extern "C" fn wrap_instance_method(
85            slf: *mut $crate::_detail::ffi::PyObject,
86            args: *mut $crate::_detail::ffi::PyObject,
87            kwargs: *mut $crate::_detail::ffi::PyObject)
88        -> *mut $crate::_detail::ffi::PyObject
89        {
90            const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
91            $crate::_detail::handle_callback(
92                LOCATION, $crate::_detail::PyObjectCallbackConverter,
93                |py| {
94                    $crate::py_argparse_raw!(py, Some(LOCATION), args, kwargs,
95                        [ $( { $pname : $ptype = $detail } )* ]
96                        {
97                            let slf = $crate::PyObject::from_borrowed_ptr(py, slf).unchecked_cast_into::<$class>();
98                            let ret = slf.$f(py $(, $pname )* );
99                            $crate::PyDrop::release_ref(slf, py);
100                            ret
101                        })
102                })
103        }
104        unsafe {
105            let method_def = $crate::py_method_def!(stringify!($f), 0, wrap_instance_method, $doc);
106            $crate::py_class::members::create_instance_method_descriptor::<$class>(method_def)
107        }
108    }}
109}
110
111pub struct InstanceMethodDescriptor<T>(*mut ffi::PyMethodDef, marker::PhantomData<fn(&T)>);
112
113#[inline]
114pub unsafe fn create_instance_method_descriptor<T>(
115    method_def: *mut ffi::PyMethodDef,
116) -> InstanceMethodDescriptor<T> {
117    InstanceMethodDescriptor(method_def, marker::PhantomData)
118}
119
120impl<T> TypeMember<T> for InstanceMethodDescriptor<T>
121where
122    T: PythonObject,
123{
124    #[inline]
125    unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
126        err::result_from_owned_ptr(py, ffi::PyDescr_NewMethod(ty, self.0))
127    }
128}
129
130#[macro_export]
131#[doc(hidden)]
132macro_rules! py_class_class_method {
133    ($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
134        $crate::py_class_class_method!($py, $class::$f, { "" } [ $( { $pname : $ptype = $detail } )* ])
135    }};
136
137    ($py:ident, $class:ident :: $f:ident, { $doc:expr } [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
138        unsafe extern "C" fn wrap_class_method(
139            cls: *mut $crate::_detail::ffi::PyObject,
140            args: *mut $crate::_detail::ffi::PyObject,
141            kwargs: *mut $crate::_detail::ffi::PyObject)
142        -> *mut $crate::_detail::ffi::PyObject
143        {
144            const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
145            $crate::_detail::handle_callback(
146                LOCATION, $crate::_detail::PyObjectCallbackConverter,
147                |py| {
148                    $crate::py_argparse_raw!(py, Some(LOCATION), args, kwargs,
149                        [ $( { $pname : $ptype = $detail } )* ]
150                        {
151                            let cls = $crate::PyObject::from_borrowed_ptr(py, cls).unchecked_cast_into::<$crate::PyType>();
152                            let ret = $class::$f(&cls, py $(, $pname )* );
153                            $crate::PyDrop::release_ref(cls, py);
154                            ret
155                        })
156                })
157        }
158        unsafe {
159            let method_def = $crate::py_method_def!(stringify!($f),
160                $crate::_detail::ffi::METH_CLASS,
161                wrap_class_method,
162                $doc);
163            $crate::py_class::members::create_class_method_descriptor(method_def)
164        }
165    }}
166}
167
168pub struct ClassMethodDescriptor(*mut ffi::PyMethodDef);
169
170#[inline]
171pub unsafe fn create_class_method_descriptor(
172    method_def: *mut ffi::PyMethodDef,
173) -> ClassMethodDescriptor {
174    ClassMethodDescriptor(method_def)
175}
176
177impl<T> TypeMember<T> for ClassMethodDescriptor
178where
179    T: PythonObject,
180{
181    #[inline]
182    unsafe fn into_descriptor(self, py: Python, ty: *mut ffi::PyTypeObject) -> PyResult<PyObject> {
183        err::result_from_owned_ptr(py, ffi::PyDescr_NewClassMethod(ty, self.0))
184    }
185}
186
187#[macro_export]
188#[doc(hidden)]
189macro_rules! py_class_static_method {
190    ($py:ident, $class:ident :: $f:ident [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
191        $crate::py_class_static_method!($py, $class::$f, { "" } [ $( { $pname : $ptype = $detail } )* ])
192    }};
193
194    ($py:ident, $class:ident :: $f:ident, { $doc:expr } [ $( { $pname:ident : $ptype:ty = $detail:tt } )* ]) => {{
195        unsafe extern "C" fn wrap_static_method(
196            _slf: *mut $crate::_detail::ffi::PyObject,
197            args: *mut $crate::_detail::ffi::PyObject,
198            kwargs: *mut $crate::_detail::ffi::PyObject)
199        -> *mut $crate::_detail::ffi::PyObject
200        {
201            const LOCATION: &'static str = concat!(stringify!($class), ".", stringify!($f), "()");
202            $crate::_detail::handle_callback(
203                LOCATION, $crate::_detail::PyObjectCallbackConverter,
204                |py| {
205                    $crate::py_argparse_raw!(py, Some(LOCATION), args, kwargs,
206                        [ $( { $pname : $ptype = $detail } )* ]
207                        {
208                            $class::$f(py $(, $pname )* )
209                        })
210                })
211        }
212        unsafe {
213            let method_def = $crate::py_method_def!(stringify!($f),
214                $crate::_detail::ffi::METH_STATIC,
215                wrap_static_method,
216                $doc);
217            $crate::_detail::py_fn_impl($py, method_def)
218        }
219    }}
220}