Skip to main content

pyo3/impl_/
pymethods.rs

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