1 | use crate::gil::LockGIL; |
2 | use crate::impl_::panic::PanicTrap; |
3 | use crate::internal_tricks::extract_c_string; |
4 | use crate::{ffi, PyAny, PyCell, PyClass, PyObject, PyResult, PyTraverseError, PyVisit, Python}; |
5 | use std::borrow::Cow; |
6 | use std::ffi::CStr; |
7 | use std::fmt; |
8 | use std::os::raw::{c_int, c_void}; |
9 | use 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)] |
14 | pub 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)] |
19 | pub struct IPowModulo(std::mem::MaybeUninit<*mut ffi::PyObject>); |
20 | |
21 | /// Helper to use as pymethod ffi definition |
22 | #[allow (non_camel_case_types)] |
23 | pub type ipowfunc = unsafe extern "C" fn( |
24 | arg1: *mut ffi::PyObject, |
25 | arg2: *mut ffi::PyObject, |
26 | arg3: IPowModulo, |
27 | ) -> *mut ffi::PyObject; |
28 | |
29 | impl 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. |
45 | pub 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)] |
61 | pub 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)] |
71 | pub struct PyCFunction(pub ffi::PyCFunction); |
72 | #[derive (Clone, Copy, Debug)] |
73 | pub struct PyCFunctionWithKeywords(pub ffi::PyCFunctionWithKeywords); |
74 | #[cfg (not(Py_LIMITED_API))] |
75 | #[derive (Clone, Copy, Debug)] |
76 | pub struct PyCFunctionFastWithKeywords(pub ffi::_PyCFunctionFastWithKeywords); |
77 | #[derive (Clone, Copy)] |
78 | pub struct PyGetter(pub Getter); |
79 | #[derive (Clone, Copy)] |
80 | pub struct PySetter(pub Setter); |
81 | #[derive (Clone, Copy)] |
82 | pub 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)] |
88 | pub 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)] |
96 | pub struct PyClassAttributeDef { |
97 | pub(crate) name: &'static str, |
98 | pub(crate) meth: PyClassAttributeFactory, |
99 | } |
100 | |
101 | impl 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)] |
108 | pub struct PyGetterDef { |
109 | pub(crate) name: &'static str, |
110 | pub(crate) meth: PyGetter, |
111 | pub(crate) doc: &'static str, |
112 | } |
113 | |
114 | #[derive (Clone)] |
115 | pub struct PySetterDef { |
116 | pub(crate) name: &'static str, |
117 | pub(crate) meth: PySetter, |
118 | pub(crate) doc: &'static str, |
119 | } |
120 | |
121 | unsafe impl Sync for PyMethodDef {} |
122 | |
123 | unsafe impl Sync for PyGetterDef {} |
124 | |
125 | unsafe impl Sync for PySetterDef {} |
126 | |
127 | impl 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 | |
200 | impl 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. |
209 | impl 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 |
218 | pub(crate) type Getter = |
219 | for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> PyResult<*mut ffi::PyObject>; |
220 | pub(crate) type Setter = |
221 | for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject, *mut ffi::PyObject) -> PyResult<c_int>; |
222 | |
223 | impl 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 | |
234 | impl 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)] |
247 | pub 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 |
253 | where |
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 | |
287 | pub(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 | |
295 | pub(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 | |
299 | pub(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 | |