1use crate::gil::LockGIL;
2use crate::impl_::panic::PanicTrap;
3use crate::internal_tricks::extract_c_string;
4use crate::{ffi, PyAny, PyCell, PyClass, PyObject, PyResult, PyTraverseError, PyVisit, Python};
5use std::borrow::Cow;
6use std::ffi::CStr;
7use std::fmt;
8use std::os::raw::{c_int, c_void};
9use std::panic::{catch_unwind, AssertUnwindSafe};
10
11/// Python 3.8 and up - __ipow__ has modulo argument correctly populated.
12#[cfg(Py_3_8)]
13#[repr(transparent)]
14pub struct IPowModulo(*mut ffi::PyObject);
15
16/// Python 3.7 and older - __ipow__ does not have modulo argument correctly populated.
17#[cfg(not(Py_3_8))]
18#[repr(transparent)]
19pub struct IPowModulo(std::mem::MaybeUninit<*mut ffi::PyObject>);
20
21/// Helper to use as pymethod ffi definition
22#[allow(non_camel_case_types)]
23pub type ipowfunc = unsafe extern "C" fn(
24 arg1: *mut ffi::PyObject,
25 arg2: *mut ffi::PyObject,
26 arg3: IPowModulo,
27) -> *mut ffi::PyObject;
28
29impl IPowModulo {
30 #[cfg(Py_3_8)]
31 #[inline]
32 pub fn to_borrowed_any(self, py: Python<'_>) -> &PyAny {
33 unsafe { py.from_borrowed_ptr::<PyAny>(self.0) }
34 }
35
36 #[cfg(not(Py_3_8))]
37 #[inline]
38 pub fn to_borrowed_any(self, py: Python<'_>) -> &PyAny {
39 unsafe { py.from_borrowed_ptr::<PyAny>(ffi::Py_None()) }
40 }
41}
42
43/// `PyMethodDefType` represents different types of Python callable objects.
44/// It is used by the `#[pymethods]` attribute.
45pub enum PyMethodDefType {
46 /// Represents class method
47 Class(PyMethodDef),
48 /// Represents static method
49 Static(PyMethodDef),
50 /// Represents normal method
51 Method(PyMethodDef),
52 /// Represents class attribute, used by `#[attribute]`
53 ClassAttribute(PyClassAttributeDef),
54 /// Represents getter descriptor, used by `#[getter]`
55 Getter(PyGetterDef),
56 /// Represents setter descriptor, used by `#[setter]`
57 Setter(PySetterDef),
58}
59
60#[derive(Copy, Clone, Debug)]
61pub enum PyMethodType {
62 PyCFunction(PyCFunction),
63 PyCFunctionWithKeywords(PyCFunctionWithKeywords),
64 #[cfg(not(Py_LIMITED_API))]
65 PyCFunctionFastWithKeywords(PyCFunctionFastWithKeywords),
66}
67
68// These newtype structs serve no purpose other than wrapping which are function pointers - because
69// function pointers aren't allowed in const fn, but types wrapping them are!
70#[derive(Clone, Copy, Debug)]
71pub struct PyCFunction(pub ffi::PyCFunction);
72#[derive(Clone, Copy, Debug)]
73pub struct PyCFunctionWithKeywords(pub ffi::PyCFunctionWithKeywords);
74#[cfg(not(Py_LIMITED_API))]
75#[derive(Clone, Copy, Debug)]
76pub struct PyCFunctionFastWithKeywords(pub ffi::_PyCFunctionFastWithKeywords);
77#[derive(Clone, Copy)]
78pub struct PyGetter(pub Getter);
79#[derive(Clone, Copy)]
80pub struct PySetter(pub Setter);
81#[derive(Clone, Copy)]
82pub struct PyClassAttributeFactory(pub for<'p> fn(Python<'p>) -> PyResult<PyObject>);
83
84// TODO: it would be nice to use CStr in these types, but then the constructors can't be const fn
85// until `CStr::from_bytes_with_nul_unchecked` is const fn.
86
87#[derive(Clone, Debug)]
88pub struct PyMethodDef {
89 pub(crate) ml_name: &'static str,
90 pub(crate) ml_meth: PyMethodType,
91 pub(crate) ml_flags: c_int,
92 pub(crate) ml_doc: &'static str,
93}
94
95#[derive(Copy, Clone)]
96pub struct PyClassAttributeDef {
97 pub(crate) name: &'static str,
98 pub(crate) meth: PyClassAttributeFactory,
99}
100
101impl PyClassAttributeDef {
102 pub(crate) fn attribute_c_string(&self) -> PyResult<Cow<'static, CStr>> {
103 extract_c_string(self.name, err_msg:"class attribute name cannot contain nul bytes")
104 }
105}
106
107#[derive(Clone)]
108pub struct PyGetterDef {
109 pub(crate) name: &'static str,
110 pub(crate) meth: PyGetter,
111 pub(crate) doc: &'static str,
112}
113
114#[derive(Clone)]
115pub struct PySetterDef {
116 pub(crate) name: &'static str,
117 pub(crate) meth: PySetter,
118 pub(crate) doc: &'static str,
119}
120
121unsafe impl Sync for PyMethodDef {}
122
123unsafe impl Sync for PyGetterDef {}
124
125unsafe impl Sync for PySetterDef {}
126
127impl PyMethodDef {
128 /// Define a function with no `*args` and `**kwargs`.
129 pub const fn noargs(name: &'static str, cfunction: PyCFunction, doc: &'static str) -> Self {
130 Self {
131 ml_name: name,
132 ml_meth: PyMethodType::PyCFunction(cfunction),
133 ml_flags: ffi::METH_NOARGS,
134 ml_doc: doc,
135 }
136 }
137
138 /// Define a function that can take `*args` and `**kwargs`.
139 pub const fn cfunction_with_keywords(
140 name: &'static str,
141 cfunction: PyCFunctionWithKeywords,
142 doc: &'static str,
143 ) -> Self {
144 Self {
145 ml_name: name,
146 ml_meth: PyMethodType::PyCFunctionWithKeywords(cfunction),
147 ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS,
148 ml_doc: doc,
149 }
150 }
151
152 /// Define a function that can take `*args` and `**kwargs`.
153 #[cfg(not(Py_LIMITED_API))]
154 pub const fn fastcall_cfunction_with_keywords(
155 name: &'static str,
156 cfunction: PyCFunctionFastWithKeywords,
157 doc: &'static str,
158 ) -> Self {
159 Self {
160 ml_name: name,
161 ml_meth: PyMethodType::PyCFunctionFastWithKeywords(cfunction),
162 ml_flags: ffi::METH_FASTCALL | ffi::METH_KEYWORDS,
163 ml_doc: doc,
164 }
165 }
166
167 pub const fn flags(mut self, flags: c_int) -> Self {
168 self.ml_flags |= flags;
169 self
170 }
171
172 /// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef`
173 pub(crate) fn as_method_def(&self) -> PyResult<(ffi::PyMethodDef, PyMethodDefDestructor)> {
174 let meth = match self.ml_meth {
175 PyMethodType::PyCFunction(meth) => ffi::PyMethodDefPointer {
176 PyCFunction: meth.0,
177 },
178 PyMethodType::PyCFunctionWithKeywords(meth) => ffi::PyMethodDefPointer {
179 PyCFunctionWithKeywords: meth.0,
180 },
181 #[cfg(not(Py_LIMITED_API))]
182 PyMethodType::PyCFunctionFastWithKeywords(meth) => ffi::PyMethodDefPointer {
183 _PyCFunctionFastWithKeywords: meth.0,
184 },
185 };
186
187 let name = get_name(self.ml_name)?;
188 let doc = get_doc(self.ml_doc)?;
189 let def = ffi::PyMethodDef {
190 ml_name: name.as_ptr(),
191 ml_meth: meth,
192 ml_flags: self.ml_flags,
193 ml_doc: doc.as_ptr(),
194 };
195 let destructor = PyMethodDefDestructor { name, doc };
196 Ok((def, destructor))
197 }
198}
199
200impl PyClassAttributeDef {
201 /// Define a class attribute.
202 pub const fn new(name: &'static str, meth: PyClassAttributeFactory) -> Self {
203 Self { name, meth }
204 }
205}
206
207// Manual implementation because `Python<'_>` does not implement `Debug` and
208// trait bounds on `fn` compiler-generated derive impls are too restrictive.
209impl fmt::Debug for PyClassAttributeDef {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 f&mut DebugStruct<'_, '_>.debug_struct("PyClassAttributeDef")
212 .field(name:"name", &self.name)
213 .finish()
214 }
215}
216
217/// Class getter / setters
218pub(crate) type Getter =
219 for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> PyResult<*mut ffi::PyObject>;
220pub(crate) type Setter =
221 for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject, *mut ffi::PyObject) -> PyResult<c_int>;
222
223impl PyGetterDef {
224 /// Define a getter.
225 pub const fn new(name: &'static str, getter: PyGetter, doc: &'static str) -> Self {
226 Self {
227 name,
228 meth: getter,
229 doc,
230 }
231 }
232}
233
234impl PySetterDef {
235 /// Define a setter.
236 pub const fn new(name: &'static str, setter: PySetter, doc: &'static str) -> Self {
237 Self {
238 name,
239 meth: setter,
240 doc,
241 }
242 }
243}
244
245/// Calls an implementation of __traverse__ for tp_traverse
246#[doc(hidden)]
247pub unsafe fn _call_traverse<T>(
248 slf: *mut ffi::PyObject,
249 impl_: fn(&T, PyVisit<'_>) -> Result<(), PyTraverseError>,
250 visit: ffi::visitproc,
251 arg: *mut c_void,
252) -> c_int
253where
254 T: PyClass,
255{
256 // It is important the implementation of `__traverse__` cannot safely access the GIL,
257 // c.f. https://github.com/PyO3/pyo3/issues/3165, and hence we do not expose our GIL
258 // token to the user code and lock safe methods for acquiring the GIL.
259 // (This includes enforcing the `&self` method receiver as e.g. `PyRef<Self>` could
260 // reconstruct a GIL token via `PyRef::py`.)
261 // Since we do not create a `GILPool` at all, it is important that our usage of the GIL
262 // token does not produce any owned objects thereby calling into `register_owned`.
263 let trap = PanicTrap::new("uncaught panic inside __traverse__ handler");
264
265 let py = Python::assume_gil_acquired();
266 let slf = py.from_borrowed_ptr::<PyCell<T>>(slf);
267 let borrow = slf.try_borrow();
268 let visit = PyVisit::from_raw(visit, arg, py);
269
270 let retval = if let Ok(borrow) = borrow {
271 let _lock = LockGIL::during_traverse();
272
273 match catch_unwind(AssertUnwindSafe(move || impl_(&*borrow, visit))) {
274 Ok(res) => match res {
275 Ok(()) => 0,
276 Err(PyTraverseError(value)) => value,
277 },
278 Err(_err) => -1,
279 }
280 } else {
281 0
282 };
283 trap.disarm();
284 retval
285}
286
287pub(crate) struct PyMethodDefDestructor {
288 // These members are just to avoid leaking CStrings when possible
289 #[allow(dead_code)]
290 name: Cow<'static, CStr>,
291 #[allow(dead_code)]
292 doc: Cow<'static, CStr>,
293}
294
295pub(crate) fn get_name(name: &'static str) -> PyResult<Cow<'static, CStr>> {
296 extract_c_string(src:name, err_msg:"function name cannot contain NUL byte.")
297}
298
299pub(crate) fn get_doc(doc: &'static str) -> PyResult<Cow<'static, CStr>> {
300 extract_c_string(src:doc, err_msg:"function doc cannot contain NUL byte.")
301}
302