1 | use crate::exceptions::PyStopAsyncIteration; |
2 | use crate::gil::LockGIL; |
3 | use crate::impl_::callback::IntoPyCallbackOutput; |
4 | use crate::impl_::panic::PanicTrap; |
5 | use crate::impl_::pycell::{PyClassObject, PyClassObjectLayout}; |
6 | use crate::internal::get_slot::{get_slot, TP_BASE, TP_CLEAR, TP_TRAVERSE}; |
7 | use crate::pycell::impl_::PyClassBorrowChecker as _; |
8 | use crate::pycell::{PyBorrowError, PyBorrowMutError}; |
9 | use crate::pyclass::boolean_struct::False; |
10 | use crate::types::any::PyAnyMethods; |
11 | use crate::types::PyType; |
12 | use crate::{ |
13 | ffi, Bound, DowncastError, Py, PyAny, PyClass, PyClassInitializer, PyErr, PyObject, PyRef, |
14 | PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python, |
15 | }; |
16 | use std::ffi::CStr; |
17 | use std::fmt; |
18 | use std::marker::PhantomData; |
19 | use std::os::raw::{c_int, c_void}; |
20 | use std::panic::{catch_unwind, AssertUnwindSafe}; |
21 | use std::ptr::null_mut; |
22 | |
23 | use super::trampoline; |
24 | use crate::internal_tricks::{clear_eq, traverse_eq}; |
25 | |
26 | /// Python 3.8 and up - __ipow__ has modulo argument correctly populated. |
27 | #[cfg (Py_3_8)] |
28 | #[repr (transparent)] |
29 | pub struct IPowModulo(*mut ffi::PyObject); |
30 | |
31 | /// Python 3.7 and older - __ipow__ does not have modulo argument correctly populated. |
32 | #[cfg (not(Py_3_8))] |
33 | #[repr (transparent)] |
34 | pub struct IPowModulo(#[allow (dead_code)] std::mem::MaybeUninit<*mut ffi::PyObject>); |
35 | |
36 | /// Helper to use as pymethod ffi definition |
37 | #[allow (non_camel_case_types)] |
38 | pub type ipowfunc = unsafe extern "C" fn( |
39 | arg1: *mut ffi::PyObject, |
40 | arg2: *mut ffi::PyObject, |
41 | arg3: IPowModulo, |
42 | ) -> *mut ffi::PyObject; |
43 | |
44 | impl IPowModulo { |
45 | #[cfg (Py_3_8)] |
46 | #[inline ] |
47 | pub fn as_ptr(self) -> *mut ffi::PyObject { |
48 | self.0 |
49 | } |
50 | |
51 | #[cfg (not(Py_3_8))] |
52 | #[inline ] |
53 | pub fn as_ptr(self) -> *mut ffi::PyObject { |
54 | // Safety: returning a borrowed pointer to Python `None` singleton |
55 | unsafe { ffi::Py_None() } |
56 | } |
57 | } |
58 | |
59 | /// `PyMethodDefType` represents different types of Python callable objects. |
60 | /// It is used by the `#[pymethods]` attribute. |
61 | #[cfg_attr (test, derive(Clone))] |
62 | pub enum PyMethodDefType { |
63 | /// Represents class method |
64 | Class(PyMethodDef), |
65 | /// Represents static method |
66 | Static(PyMethodDef), |
67 | /// Represents normal method |
68 | Method(PyMethodDef), |
69 | /// Represents class attribute, used by `#[attribute]` |
70 | ClassAttribute(PyClassAttributeDef), |
71 | /// Represents getter descriptor, used by `#[getter]` |
72 | Getter(PyGetterDef), |
73 | /// Represents setter descriptor, used by `#[setter]` |
74 | Setter(PySetterDef), |
75 | /// Represents a struct member |
76 | StructMember(ffi::PyMemberDef), |
77 | } |
78 | |
79 | #[derive (Copy, Clone, Debug)] |
80 | pub enum PyMethodType { |
81 | PyCFunction(ffi::PyCFunction), |
82 | PyCFunctionWithKeywords(ffi::PyCFunctionWithKeywords), |
83 | #[cfg (any(Py_3_10, not(Py_LIMITED_API)))] |
84 | PyCFunctionFastWithKeywords(ffi::PyCFunctionFastWithKeywords), |
85 | } |
86 | |
87 | pub type PyClassAttributeFactory = for<'p> fn(Python<'p>) -> PyResult<PyObject>; |
88 | |
89 | // TODO: it would be nice to use CStr in these types, but then the constructors can't be const fn |
90 | // until `CStr::from_bytes_with_nul_unchecked` is const fn. |
91 | |
92 | #[derive (Clone, Debug)] |
93 | pub struct PyMethodDef { |
94 | pub(crate) ml_name: &'static CStr, |
95 | pub(crate) ml_meth: PyMethodType, |
96 | pub(crate) ml_flags: c_int, |
97 | pub(crate) ml_doc: &'static CStr, |
98 | } |
99 | |
100 | #[derive (Copy, Clone)] |
101 | pub struct PyClassAttributeDef { |
102 | pub(crate) name: &'static CStr, |
103 | pub(crate) meth: PyClassAttributeFactory, |
104 | } |
105 | |
106 | #[derive (Clone)] |
107 | pub struct PyGetterDef { |
108 | pub(crate) name: &'static CStr, |
109 | pub(crate) meth: Getter, |
110 | pub(crate) doc: &'static CStr, |
111 | } |
112 | |
113 | #[derive (Clone)] |
114 | pub struct PySetterDef { |
115 | pub(crate) name: &'static CStr, |
116 | pub(crate) meth: Setter, |
117 | pub(crate) doc: &'static CStr, |
118 | } |
119 | |
120 | unsafe impl Sync for PyMethodDef {} |
121 | |
122 | unsafe impl Sync for PyGetterDef {} |
123 | |
124 | unsafe impl Sync for PySetterDef {} |
125 | |
126 | impl PyMethodDef { |
127 | /// Define a function with no `*args` and `**kwargs`. |
128 | pub const fn noargs( |
129 | ml_name: &'static CStr, |
130 | cfunction: ffi::PyCFunction, |
131 | ml_doc: &'static CStr, |
132 | ) -> Self { |
133 | Self { |
134 | ml_name, |
135 | ml_meth: PyMethodType::PyCFunction(cfunction), |
136 | ml_flags: ffi::METH_NOARGS, |
137 | ml_doc, |
138 | } |
139 | } |
140 | |
141 | /// Define a function that can take `*args` and `**kwargs`. |
142 | pub const fn cfunction_with_keywords( |
143 | ml_name: &'static CStr, |
144 | cfunction: ffi::PyCFunctionWithKeywords, |
145 | ml_doc: &'static CStr, |
146 | ) -> Self { |
147 | Self { |
148 | ml_name, |
149 | ml_meth: PyMethodType::PyCFunctionWithKeywords(cfunction), |
150 | ml_flags: ffi::METH_VARARGS | ffi::METH_KEYWORDS, |
151 | ml_doc, |
152 | } |
153 | } |
154 | |
155 | /// Define a function that can take `*args` and `**kwargs`. |
156 | #[cfg (any(Py_3_10, not(Py_LIMITED_API)))] |
157 | pub const fn fastcall_cfunction_with_keywords( |
158 | ml_name: &'static CStr, |
159 | cfunction: ffi::PyCFunctionFastWithKeywords, |
160 | ml_doc: &'static CStr, |
161 | ) -> Self { |
162 | Self { |
163 | ml_name, |
164 | ml_meth: PyMethodType::PyCFunctionFastWithKeywords(cfunction), |
165 | ml_flags: ffi::METH_FASTCALL | ffi::METH_KEYWORDS, |
166 | ml_doc, |
167 | } |
168 | } |
169 | |
170 | pub const fn flags(mut self, flags: c_int) -> Self { |
171 | self.ml_flags |= flags; |
172 | self |
173 | } |
174 | |
175 | /// Convert `PyMethodDef` to Python method definition struct `ffi::PyMethodDef` |
176 | pub(crate) fn as_method_def(&self) -> ffi::PyMethodDef { |
177 | let meth = match self.ml_meth { |
178 | PyMethodType::PyCFunction(meth) => ffi::PyMethodDefPointer { PyCFunction: meth }, |
179 | PyMethodType::PyCFunctionWithKeywords(meth) => ffi::PyMethodDefPointer { |
180 | PyCFunctionWithKeywords: meth, |
181 | }, |
182 | #[cfg (any(Py_3_10, not(Py_LIMITED_API)))] |
183 | PyMethodType::PyCFunctionFastWithKeywords(meth) => ffi::PyMethodDefPointer { |
184 | PyCFunctionFastWithKeywords: meth, |
185 | }, |
186 | }; |
187 | |
188 | ffi::PyMethodDef { |
189 | ml_name: self.ml_name.as_ptr(), |
190 | ml_meth: meth, |
191 | ml_flags: self.ml_flags, |
192 | ml_doc: self.ml_doc.as_ptr(), |
193 | } |
194 | } |
195 | } |
196 | |
197 | impl PyClassAttributeDef { |
198 | /// Define a class attribute. |
199 | pub const fn new(name: &'static CStr, meth: PyClassAttributeFactory) -> Self { |
200 | Self { name, meth } |
201 | } |
202 | } |
203 | |
204 | // Manual implementation because `Python<'_>` does not implement `Debug` and |
205 | // trait bounds on `fn` compiler-generated derive impls are too restrictive. |
206 | impl fmt::Debug for PyClassAttributeDef { |
207 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
208 | f&mut DebugStruct<'_, '_>.debug_struct("PyClassAttributeDef" ) |
209 | .field(name:"name" , &self.name) |
210 | .finish() |
211 | } |
212 | } |
213 | |
214 | /// Class getter / setters |
215 | pub(crate) type Getter = |
216 | for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> PyResult<*mut ffi::PyObject>; |
217 | pub(crate) type Setter = |
218 | for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject, *mut ffi::PyObject) -> PyResult<c_int>; |
219 | |
220 | impl PyGetterDef { |
221 | /// Define a getter. |
222 | pub const fn new(name: &'static CStr, getter: Getter, doc: &'static CStr) -> Self { |
223 | Self { |
224 | name, |
225 | meth: getter, |
226 | doc, |
227 | } |
228 | } |
229 | } |
230 | |
231 | impl PySetterDef { |
232 | /// Define a setter. |
233 | pub const fn new(name: &'static CStr, setter: Setter, doc: &'static CStr) -> Self { |
234 | Self { |
235 | name, |
236 | meth: setter, |
237 | doc, |
238 | } |
239 | } |
240 | } |
241 | |
242 | /// Calls an implementation of __traverse__ for tp_traverse |
243 | /// |
244 | /// NB cannot accept `'static` visitor, this is a sanity check below: |
245 | /// |
246 | /// ```rust,compile_fail |
247 | /// use pyo3::prelude::*; |
248 | /// use pyo3::pyclass::{PyTraverseError, PyVisit}; |
249 | /// |
250 | /// #[pyclass] |
251 | /// struct Foo; |
252 | /// |
253 | /// #[pymethods] |
254 | /// impl Foo { |
255 | /// fn __traverse__(&self, _visit: PyVisit<'static>) -> Result<(), PyTraverseError> { |
256 | /// Ok(()) |
257 | /// } |
258 | /// } |
259 | /// ``` |
260 | /// |
261 | /// Elided lifetime should compile ok: |
262 | /// |
263 | /// ```rust |
264 | /// use pyo3::prelude::*; |
265 | /// use pyo3::pyclass::{PyTraverseError, PyVisit}; |
266 | /// |
267 | /// #[pyclass] |
268 | /// struct Foo; |
269 | /// |
270 | /// #[pymethods] |
271 | /// impl Foo { |
272 | /// fn __traverse__(&self, _visit: PyVisit<'_>) -> Result<(), PyTraverseError> { |
273 | /// Ok(()) |
274 | /// } |
275 | /// } |
276 | /// ``` |
277 | #[doc (hidden)] |
278 | pub unsafe fn _call_traverse<T>( |
279 | slf: *mut ffi::PyObject, |
280 | impl_: fn(&T, PyVisit<'_>) -> Result<(), PyTraverseError>, |
281 | visit: ffi::visitproc, |
282 | arg: *mut c_void, |
283 | current_traverse: ffi::traverseproc, |
284 | ) -> c_int |
285 | where |
286 | T: PyClass, |
287 | { |
288 | // It is important the implementation of `__traverse__` cannot safely access the GIL, |
289 | // c.f. https://github.com/PyO3/pyo3/issues/3165, and hence we do not expose our GIL |
290 | // token to the user code and lock safe methods for acquiring the GIL. |
291 | // (This includes enforcing the `&self` method receiver as e.g. `PyRef<Self>` could |
292 | // reconstruct a GIL token via `PyRef::py`.) |
293 | // Since we do not create a `GILPool` at all, it is important that our usage of the GIL |
294 | // token does not produce any owned objects thereby calling into `register_owned`. |
295 | let trap = PanicTrap::new("uncaught panic inside __traverse__ handler" ); |
296 | let lock = LockGIL::during_traverse(); |
297 | |
298 | let super_retval = unsafe { call_super_traverse(slf, visit, arg, current_traverse) }; |
299 | if super_retval != 0 { |
300 | return super_retval; |
301 | } |
302 | |
303 | // SAFETY: `slf` is a valid Python object pointer to a class object of type T, and |
304 | // traversal is running so no mutations can occur. |
305 | let class_object: &PyClassObject<T> = unsafe { &*slf.cast() }; |
306 | |
307 | let retval = |
308 | // `#[pyclass(unsendable)]` types can only be deallocated by their own thread, so |
309 | // do not traverse them if not on their owning thread :( |
310 | if class_object.check_threadsafe().is_ok() |
311 | // ... and we cannot traverse a type which might be being mutated by a Rust thread |
312 | && class_object.borrow_checker().try_borrow().is_ok() { |
313 | struct TraverseGuard<'a, T: PyClass>(&'a PyClassObject<T>); |
314 | impl<T: PyClass> Drop for TraverseGuard<'_, T> { |
315 | fn drop(&mut self) { |
316 | self.0.borrow_checker().release_borrow() |
317 | } |
318 | } |
319 | |
320 | // `.try_borrow()` above created a borrow, we need to release it when we're done |
321 | // traversing the object. This allows us to read `instance` safely. |
322 | let _guard = TraverseGuard(class_object); |
323 | let instance = unsafe {&*class_object.contents.value.get()}; |
324 | |
325 | let visit = PyVisit { visit, arg, _guard: PhantomData }; |
326 | |
327 | match catch_unwind(AssertUnwindSafe(move || impl_(instance, visit))) { |
328 | Ok(Ok(())) => 0, |
329 | Ok(Err(traverse_error)) => traverse_error.into_inner(), |
330 | Err(_err) => -1, |
331 | } |
332 | } else { |
333 | 0 |
334 | }; |
335 | |
336 | // Drop lock before trap just in case dropping lock panics |
337 | drop(lock); |
338 | trap.disarm(); |
339 | retval |
340 | } |
341 | |
342 | /// Call super-type traverse method, if necessary. |
343 | /// |
344 | /// Adapted from <https://github.com/cython/cython/blob/7acfb375fb54a033f021b0982a3cd40c34fb22ac/Cython/Utility/ExtensionTypes.c#L386> |
345 | /// |
346 | /// TODO: There are possible optimizations over looking up the base type in this way |
347 | /// - if the base type is known in this module, can potentially look it up directly in module state |
348 | /// (when we have it) |
349 | /// - if the base type is a Python builtin, can jut call the C function directly |
350 | /// - if the base type is a PyO3 type defined in the same module, can potentially do similar to |
351 | /// tp_alloc where we solve this at compile time |
352 | unsafe fn call_super_traverse( |
353 | obj: *mut ffi::PyObject, |
354 | visit: ffi::visitproc, |
355 | arg: *mut c_void, |
356 | current_traverse: ffi::traverseproc, |
357 | ) -> c_int { |
358 | // SAFETY: in this function here it's ok to work with raw type objects `ffi::Py_TYPE` |
359 | // because the GC is running and so |
360 | // - (a) we cannot do refcounting and |
361 | // - (b) the type of the object cannot change. |
362 | let mut ty = unsafe { ffi::Py_TYPE(obj) }; |
363 | let mut traverse: Option<ffi::traverseproc>; |
364 | |
365 | // First find the current type by the current_traverse function |
366 | loop { |
367 | traverse = unsafe { get_slot(ty, TP_TRAVERSE) }; |
368 | if traverse_eq(traverse, current_traverse) { |
369 | break; |
370 | } |
371 | ty = unsafe { get_slot(ty, TP_BASE) }; |
372 | if ty.is_null() { |
373 | // FIXME: return an error if current type not in the MRO? Should be impossible. |
374 | return 0; |
375 | } |
376 | } |
377 | |
378 | // Get first base which has a different traverse function |
379 | while traverse_eq(traverse, current_traverse) { |
380 | ty = unsafe { get_slot(ty, TP_BASE) }; |
381 | if ty.is_null() { |
382 | break; |
383 | } |
384 | traverse = unsafe { get_slot(ty, TP_TRAVERSE) }; |
385 | } |
386 | |
387 | // If we found a type with a different traverse function, call it |
388 | if let Some(traverse) = traverse { |
389 | return unsafe { traverse(obj, visit, arg) }; |
390 | } |
391 | |
392 | // FIXME same question as cython: what if the current type is not in the MRO? |
393 | 0 |
394 | } |
395 | |
396 | /// Calls an implementation of __clear__ for tp_clear |
397 | pub unsafe fn _call_clear( |
398 | slf: *mut ffi::PyObject, |
399 | impl_: for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> PyResult<()>, |
400 | current_clear: ffi::inquiry, |
401 | ) -> c_int { |
402 | unsafe { |
403 | trampoline::trampoline(body:move |py: Python<'_>| { |
404 | let super_retval = call_super_clear(py, obj:slf, current_clear); |
405 | if super_retval != 0 { |
406 | return Err(PyErr::fetch(py)); |
407 | } |
408 | impl_(py, slf)?; |
409 | Ok(0) |
410 | }) |
411 | } |
412 | } |
413 | |
414 | /// Call super-type traverse method, if necessary. |
415 | /// |
416 | /// Adapted from <https://github.com/cython/cython/blob/7acfb375fb54a033f021b0982a3cd40c34fb22ac/Cython/Utility/ExtensionTypes.c#L386> |
417 | /// |
418 | /// TODO: There are possible optimizations over looking up the base type in this way |
419 | /// - if the base type is known in this module, can potentially look it up directly in module state |
420 | /// (when we have it) |
421 | /// - if the base type is a Python builtin, can jut call the C function directly |
422 | /// - if the base type is a PyO3 type defined in the same module, can potentially do similar to |
423 | /// tp_alloc where we solve this at compile time |
424 | unsafe fn call_super_clear( |
425 | py: Python<'_>, |
426 | obj: *mut ffi::PyObject, |
427 | current_clear: ffi::inquiry, |
428 | ) -> c_int { |
429 | let mut ty = unsafe { PyType::from_borrowed_type_ptr(py, ffi::Py_TYPE(obj)) }; |
430 | let mut clear: Option<ffi::inquiry>; |
431 | |
432 | // First find the current type by the current_clear function |
433 | loop { |
434 | clear = ty.get_slot(TP_CLEAR); |
435 | if clear_eq(clear, current_clear) { |
436 | break; |
437 | } |
438 | let base = ty.get_slot(TP_BASE); |
439 | if base.is_null() { |
440 | // FIXME: return an error if current type not in the MRO? Should be impossible. |
441 | return 0; |
442 | } |
443 | ty = unsafe { PyType::from_borrowed_type_ptr(py, base) }; |
444 | } |
445 | |
446 | // Get first base which has a different clear function |
447 | while clear_eq(clear, current_clear) { |
448 | let base = ty.get_slot(TP_BASE); |
449 | if base.is_null() { |
450 | break; |
451 | } |
452 | ty = unsafe { PyType::from_borrowed_type_ptr(py, base) }; |
453 | clear = ty.get_slot(TP_CLEAR); |
454 | } |
455 | |
456 | // If we found a type with a different clear function, call it |
457 | if let Some(clear) = clear { |
458 | return unsafe { clear(obj) }; |
459 | } |
460 | |
461 | // FIXME same question as cython: what if the current type is not in the MRO? |
462 | 0 |
463 | } |
464 | |
465 | // Autoref-based specialization for handling `__next__` returning `Option` |
466 | |
467 | pub struct IterBaseTag; |
468 | |
469 | impl IterBaseTag { |
470 | #[inline ] |
471 | pub fn convert<'py, Value, Target>(self, py: Python<'py>, value: Value) -> PyResult<Target> |
472 | where |
473 | Value: IntoPyCallbackOutput<'py, Target>, |
474 | { |
475 | value.convert(py) |
476 | } |
477 | } |
478 | |
479 | pub trait IterBaseKind { |
480 | #[inline ] |
481 | fn iter_tag(&self) -> IterBaseTag { |
482 | IterBaseTag |
483 | } |
484 | } |
485 | |
486 | impl<Value> IterBaseKind for &Value {} |
487 | |
488 | pub struct IterOptionTag; |
489 | |
490 | impl IterOptionTag { |
491 | #[inline ] |
492 | pub fn convert<'py, Value>( |
493 | self, |
494 | py: Python<'py>, |
495 | value: Option<Value>, |
496 | ) -> PyResult<*mut ffi::PyObject> |
497 | where |
498 | Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>, |
499 | { |
500 | match value { |
501 | Some(value: Value) => value.convert(py), |
502 | None => Ok(null_mut()), |
503 | } |
504 | } |
505 | } |
506 | |
507 | pub trait IterOptionKind { |
508 | #[inline ] |
509 | fn iter_tag(&self) -> IterOptionTag { |
510 | IterOptionTag |
511 | } |
512 | } |
513 | |
514 | impl<Value> IterOptionKind for Option<Value> {} |
515 | |
516 | pub struct IterResultOptionTag; |
517 | |
518 | impl IterResultOptionTag { |
519 | #[inline ] |
520 | pub fn convert<'py, Value, Error>( |
521 | self, |
522 | py: Python<'py>, |
523 | value: Result<Option<Value>, Error>, |
524 | ) -> PyResult<*mut ffi::PyObject> |
525 | where |
526 | Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>, |
527 | Error: Into<PyErr>, |
528 | { |
529 | match value { |
530 | Ok(Some(value: Value)) => value.convert(py), |
531 | Ok(None) => Ok(null_mut()), |
532 | Err(err: Error) => Err(err.into()), |
533 | } |
534 | } |
535 | } |
536 | |
537 | pub trait IterResultOptionKind { |
538 | #[inline ] |
539 | fn iter_tag(&self) -> IterResultOptionTag { |
540 | IterResultOptionTag |
541 | } |
542 | } |
543 | |
544 | impl<Value, Error> IterResultOptionKind for Result<Option<Value>, Error> {} |
545 | |
546 | // Autoref-based specialization for handling `__anext__` returning `Option` |
547 | |
548 | pub struct AsyncIterBaseTag; |
549 | |
550 | impl AsyncIterBaseTag { |
551 | #[inline ] |
552 | pub fn convert<'py, Value, Target>(self, py: Python<'py>, value: Value) -> PyResult<Target> |
553 | where |
554 | Value: IntoPyCallbackOutput<'py, Target>, |
555 | { |
556 | value.convert(py) |
557 | } |
558 | } |
559 | |
560 | pub trait AsyncIterBaseKind { |
561 | #[inline ] |
562 | fn async_iter_tag(&self) -> AsyncIterBaseTag { |
563 | AsyncIterBaseTag |
564 | } |
565 | } |
566 | |
567 | impl<Value> AsyncIterBaseKind for &Value {} |
568 | |
569 | pub struct AsyncIterOptionTag; |
570 | |
571 | impl AsyncIterOptionTag { |
572 | #[inline ] |
573 | pub fn convert<'py, Value>( |
574 | self, |
575 | py: Python<'py>, |
576 | value: Option<Value>, |
577 | ) -> PyResult<*mut ffi::PyObject> |
578 | where |
579 | Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>, |
580 | { |
581 | match value { |
582 | Some(value: Value) => value.convert(py), |
583 | None => Err(PyStopAsyncIteration::new_err(())), |
584 | } |
585 | } |
586 | } |
587 | |
588 | pub trait AsyncIterOptionKind { |
589 | #[inline ] |
590 | fn async_iter_tag(&self) -> AsyncIterOptionTag { |
591 | AsyncIterOptionTag |
592 | } |
593 | } |
594 | |
595 | impl<Value> AsyncIterOptionKind for Option<Value> {} |
596 | |
597 | pub struct AsyncIterResultOptionTag; |
598 | |
599 | impl AsyncIterResultOptionTag { |
600 | #[inline ] |
601 | pub fn convert<'py, Value, Error>( |
602 | self, |
603 | py: Python<'py>, |
604 | value: Result<Option<Value>, Error>, |
605 | ) -> PyResult<*mut ffi::PyObject> |
606 | where |
607 | Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>, |
608 | Error: Into<PyErr>, |
609 | { |
610 | match value { |
611 | Ok(Some(value: Value)) => value.convert(py), |
612 | Ok(None) => Err(PyStopAsyncIteration::new_err(())), |
613 | Err(err: Error) => Err(err.into()), |
614 | } |
615 | } |
616 | } |
617 | |
618 | pub trait AsyncIterResultOptionKind { |
619 | #[inline ] |
620 | fn async_iter_tag(&self) -> AsyncIterResultOptionTag { |
621 | AsyncIterResultOptionTag |
622 | } |
623 | } |
624 | |
625 | impl<Value, Error> AsyncIterResultOptionKind for Result<Option<Value>, Error> {} |
626 | |
627 | /// Used in `#[classmethod]` to pass the class object to the method |
628 | /// and also in `#[pyfunction(pass_module)]`. |
629 | /// |
630 | /// This is a wrapper to avoid implementing `From<Bound>` for GIL Refs. |
631 | /// |
632 | /// Once the GIL Ref API is fully removed, it should be possible to simplify |
633 | /// this to just `&'a Bound<'py, T>` and `From` implementations. |
634 | pub struct BoundRef<'a, 'py, T>(pub &'a Bound<'py, T>); |
635 | |
636 | impl<'a, 'py> BoundRef<'a, 'py, PyAny> { |
637 | pub unsafe fn ref_from_ptr(py: Python<'py>, ptr: &'a *mut ffi::PyObject) -> Self { |
638 | unsafe { BoundRef(Bound::ref_from_ptr(py, ptr)) } |
639 | } |
640 | |
641 | pub unsafe fn ref_from_ptr_or_opt( |
642 | py: Python<'py>, |
643 | ptr: &'a *mut ffi::PyObject, |
644 | ) -> Option<Self> { |
645 | unsafe { Bound::ref_from_ptr_or_opt(py, ptr).as_ref().map(BoundRef) } |
646 | } |
647 | |
648 | pub fn downcast<T: PyTypeCheck>(self) -> Result<BoundRef<'a, 'py, T>, DowncastError<'a, 'py>> { |
649 | self.0.downcast::<T>().map(op:BoundRef) |
650 | } |
651 | |
652 | pub unsafe fn downcast_unchecked<T>(self) -> BoundRef<'a, 'py, T> { |
653 | unsafe { BoundRef(self.0.downcast_unchecked::<T>()) } |
654 | } |
655 | } |
656 | |
657 | impl<'a, 'py, T: PyClass> TryFrom<BoundRef<'a, 'py, T>> for PyRef<'py, T> { |
658 | type Error = PyBorrowError; |
659 | #[inline ] |
660 | fn try_from(value: BoundRef<'a, 'py, T>) -> Result<Self, Self::Error> { |
661 | value.0.try_borrow() |
662 | } |
663 | } |
664 | |
665 | impl<'a, 'py, T: PyClass<Frozen = False>> TryFrom<BoundRef<'a, 'py, T>> for PyRefMut<'py, T> { |
666 | type Error = PyBorrowMutError; |
667 | #[inline ] |
668 | fn try_from(value: BoundRef<'a, 'py, T>) -> Result<Self, Self::Error> { |
669 | value.0.try_borrow_mut() |
670 | } |
671 | } |
672 | |
673 | impl<'a, 'py, T> From<BoundRef<'a, 'py, T>> for Bound<'py, T> { |
674 | #[inline ] |
675 | fn from(bound: BoundRef<'a, 'py, T>) -> Self { |
676 | bound.0.clone() |
677 | } |
678 | } |
679 | |
680 | impl<'a, 'py, T> From<BoundRef<'a, 'py, T>> for &'a Bound<'py, T> { |
681 | #[inline ] |
682 | fn from(bound: BoundRef<'a, 'py, T>) -> Self { |
683 | bound.0 |
684 | } |
685 | } |
686 | |
687 | impl<T> From<BoundRef<'_, '_, T>> for Py<T> { |
688 | #[inline ] |
689 | fn from(bound: BoundRef<'_, '_, T>) -> Self { |
690 | bound.0.clone().unbind() |
691 | } |
692 | } |
693 | |
694 | impl<'py, T> std::ops::Deref for BoundRef<'_, 'py, T> { |
695 | type Target = Bound<'py, T>; |
696 | #[inline ] |
697 | fn deref(&self) -> &Self::Target { |
698 | self.0 |
699 | } |
700 | } |
701 | |
702 | pub unsafe fn tp_new_impl<T: PyClass>( |
703 | py: Python<'_>, |
704 | initializer: PyClassInitializer<T>, |
705 | target_type: *mut ffi::PyTypeObject, |
706 | ) -> PyResult<*mut ffi::PyObject> { |
707 | unsafe { |
708 | initializer |
709 | .create_class_object_of_type(py, target_type) |
710 | .map(op:Bound::into_ptr) |
711 | } |
712 | } |
713 | |
714 | #[cfg (test)] |
715 | mod tests { |
716 | #[test ] |
717 | #[cfg (any(Py_3_10, not(Py_LIMITED_API)))] |
718 | fn test_fastcall_function_with_keywords() { |
719 | use super::PyMethodDef; |
720 | use crate::types::{PyAnyMethods, PyCFunction}; |
721 | use crate::{ffi, Python}; |
722 | |
723 | Python::with_gil(|py| { |
724 | unsafe extern "C" fn accepts_no_arguments( |
725 | _slf: *mut ffi::PyObject, |
726 | _args: *const *mut ffi::PyObject, |
727 | nargs: ffi::Py_ssize_t, |
728 | kwargs: *mut ffi::PyObject, |
729 | ) -> *mut ffi::PyObject { |
730 | assert_eq!(nargs, 0); |
731 | assert!(kwargs.is_null()); |
732 | unsafe { Python::assume_gil_acquired().None().into_ptr() } |
733 | } |
734 | |
735 | let f = PyCFunction::internal_new( |
736 | py, |
737 | &PyMethodDef::fastcall_cfunction_with_keywords( |
738 | ffi::c_str!("test" ), |
739 | accepts_no_arguments, |
740 | ffi::c_str!("doc" ), |
741 | ), |
742 | None, |
743 | ) |
744 | .unwrap(); |
745 | |
746 | f.call0().unwrap(); |
747 | }); |
748 | } |
749 | } |
750 | |