1use crate::exceptions::PyStopAsyncIteration;
2use crate::gil::LockGIL;
3use crate::impl_::callback::IntoPyCallbackOutput;
4use crate::impl_::panic::PanicTrap;
5use crate::impl_::pycell::{PyClassObject, PyClassObjectLayout};
6use crate::internal::get_slot::{get_slot, TP_BASE, TP_CLEAR, TP_TRAVERSE};
7use crate::pycell::impl_::PyClassBorrowChecker as _;
8use crate::pycell::{PyBorrowError, PyBorrowMutError};
9use crate::pyclass::boolean_struct::False;
10use crate::types::any::PyAnyMethods;
11use crate::types::PyType;
12use crate::{
13 ffi, Bound, DowncastError, Py, PyAny, PyClass, PyClassInitializer, PyErr, PyObject, PyRef,
14 PyRefMut, PyResult, PyTraverseError, PyTypeCheck, PyVisit, Python,
15};
16use std::ffi::CStr;
17use std::fmt;
18use std::marker::PhantomData;
19use std::os::raw::{c_int, c_void};
20use std::panic::{catch_unwind, AssertUnwindSafe};
21use std::ptr::null_mut;
22
23use super::trampoline;
24use crate::internal_tricks::{clear_eq, traverse_eq};
25
26#[cfg(Py_3_8)]
28#[repr(transparent)]
29pub struct IPowModulo(*mut ffi::PyObject);
30
31#[cfg(not(Py_3_8))]
33#[repr(transparent)]
34pub struct IPowModulo(#[allow(dead_code)] std::mem::MaybeUninit<*mut ffi::PyObject>);
35
36#[allow(non_camel_case_types)]
38pub type ipowfunc = unsafe extern "C" fn(
39 arg1: *mut ffi::PyObject,
40 arg2: *mut ffi::PyObject,
41 arg3: IPowModulo,
42) -> *mut ffi::PyObject;
43
44impl 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 unsafe { ffi::Py_None() }
56 }
57}
58
59#[cfg_attr(test, derive(Clone))]
62pub enum PyMethodDefType {
63 Class(PyMethodDef),
65 Static(PyMethodDef),
67 Method(PyMethodDef),
69 ClassAttribute(PyClassAttributeDef),
71 Getter(PyGetterDef),
73 Setter(PySetterDef),
75 StructMember(ffi::PyMemberDef),
77}
78
79#[derive(Copy, Clone, Debug)]
80pub 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
87pub type PyClassAttributeFactory = for<'p> fn(Python<'p>) -> PyResult<PyObject>;
88
89#[derive(Clone, Debug)]
93pub 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)]
101pub struct PyClassAttributeDef {
102 pub(crate) name: &'static CStr,
103 pub(crate) meth: PyClassAttributeFactory,
104}
105
106#[derive(Clone)]
107pub struct PyGetterDef {
108 pub(crate) name: &'static CStr,
109 pub(crate) meth: Getter,
110 pub(crate) doc: &'static CStr,
111}
112
113#[derive(Clone)]
114pub struct PySetterDef {
115 pub(crate) name: &'static CStr,
116 pub(crate) meth: Setter,
117 pub(crate) doc: &'static CStr,
118}
119
120unsafe impl Sync for PyMethodDef {}
121
122unsafe impl Sync for PyGetterDef {}
123
124unsafe impl Sync for PySetterDef {}
125
126impl PyMethodDef {
127 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 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 #[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 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
197impl PyClassAttributeDef {
198 pub const fn new(name: &'static CStr, meth: PyClassAttributeFactory) -> Self {
200 Self { name, meth }
201 }
202}
203
204impl fmt::Debug for PyClassAttributeDef {
207 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
208 f.debug_struct("PyClassAttributeDef")
209 .field("name", &self.name)
210 .finish()
211 }
212}
213
214pub(crate) type Getter =
216 for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject) -> PyResult<*mut ffi::PyObject>;
217pub(crate) type Setter =
218 for<'py> unsafe fn(Python<'py>, *mut ffi::PyObject, *mut ffi::PyObject) -> PyResult<c_int>;
219
220impl PyGetterDef {
221 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
231impl PySetterDef {
232 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#[doc(hidden)]
278pub 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
285where
286 T: PyClass,
287{
288 let trap = PanicTrap::new("uncaught panic inside __traverse__ handler");
296 let lock = LockGIL::during_traverse();
297
298 let super_retval = call_super_traverse(slf, visit, arg, current_traverse);
299 if super_retval != 0 {
300 return super_retval;
301 }
302
303 let class_object: &PyClassObject<T> = &*slf.cast();
306
307 let retval =
308 if class_object.check_threadsafe().is_ok()
311 && 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 let _guard = TraverseGuard(class_object);
323 let instance = &*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);
338 trap.disarm();
339 retval
340}
341
342unsafe 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 let mut ty = ffi::Py_TYPE(obj);
363 let mut traverse: Option<ffi::traverseproc>;
364
365 loop {
367 traverse = get_slot(ty, TP_TRAVERSE);
368 if traverse_eq(traverse, current_traverse) {
369 break;
370 }
371 ty = get_slot(ty, TP_BASE);
372 if ty.is_null() {
373 return 0;
375 }
376 }
377
378 while traverse_eq(traverse, current_traverse) {
380 ty = get_slot(ty, TP_BASE);
381 if ty.is_null() {
382 break;
383 }
384 traverse = get_slot(ty, TP_TRAVERSE);
385 }
386
387 if let Some(traverse) = traverse {
389 return traverse(obj, visit, arg);
390 }
391
392 0
394}
395
396pub 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 trampoline::trampoline(move |py| {
403 let super_retval = call_super_clear(py, slf, current_clear);
404 if super_retval != 0 {
405 return Err(PyErr::fetch(py));
406 }
407 impl_(py, slf)?;
408 Ok(0)
409 })
410}
411
412unsafe fn call_super_clear(
423 py: Python<'_>,
424 obj: *mut ffi::PyObject,
425 current_clear: ffi::inquiry,
426) -> c_int {
427 let mut ty = PyType::from_borrowed_type_ptr(py, ffi::Py_TYPE(obj));
428 let mut clear: Option<ffi::inquiry>;
429
430 loop {
432 clear = ty.get_slot(TP_CLEAR);
433 if clear_eq(clear, current_clear) {
434 break;
435 }
436 let base = ty.get_slot(TP_BASE);
437 if base.is_null() {
438 return 0;
440 }
441 ty = PyType::from_borrowed_type_ptr(py, base);
442 }
443
444 while clear_eq(clear, current_clear) {
446 let base = ty.get_slot(TP_BASE);
447 if base.is_null() {
448 break;
449 }
450 ty = PyType::from_borrowed_type_ptr(py, base);
451 clear = ty.get_slot(TP_CLEAR);
452 }
453
454 if let Some(clear) = clear {
456 return clear(obj);
457 }
458
459 0
461}
462
463pub struct IterBaseTag;
466
467impl IterBaseTag {
468 #[inline]
469 pub fn convert<'py, Value, Target>(self, py: Python<'py>, value: Value) -> PyResult<Target>
470 where
471 Value: IntoPyCallbackOutput<'py, Target>,
472 {
473 value.convert(py)
474 }
475}
476
477pub trait IterBaseKind {
478 #[inline]
479 fn iter_tag(&self) -> IterBaseTag {
480 IterBaseTag
481 }
482}
483
484impl<Value> IterBaseKind for &Value {}
485
486pub struct IterOptionTag;
487
488impl IterOptionTag {
489 #[inline]
490 pub fn convert<'py, Value>(
491 self,
492 py: Python<'py>,
493 value: Option<Value>,
494 ) -> PyResult<*mut ffi::PyObject>
495 where
496 Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
497 {
498 match value {
499 Some(value) => value.convert(py),
500 None => Ok(null_mut()),
501 }
502 }
503}
504
505pub trait IterOptionKind {
506 #[inline]
507 fn iter_tag(&self) -> IterOptionTag {
508 IterOptionTag
509 }
510}
511
512impl<Value> IterOptionKind for Option<Value> {}
513
514pub struct IterResultOptionTag;
515
516impl IterResultOptionTag {
517 #[inline]
518 pub fn convert<'py, Value, Error>(
519 self,
520 py: Python<'py>,
521 value: Result<Option<Value>, Error>,
522 ) -> PyResult<*mut ffi::PyObject>
523 where
524 Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
525 Error: Into<PyErr>,
526 {
527 match value {
528 Ok(Some(value)) => value.convert(py),
529 Ok(None) => Ok(null_mut()),
530 Err(err) => Err(err.into()),
531 }
532 }
533}
534
535pub trait IterResultOptionKind {
536 #[inline]
537 fn iter_tag(&self) -> IterResultOptionTag {
538 IterResultOptionTag
539 }
540}
541
542impl<Value, Error> IterResultOptionKind for Result<Option<Value>, Error> {}
543
544pub struct AsyncIterBaseTag;
547
548impl AsyncIterBaseTag {
549 #[inline]
550 pub fn convert<'py, Value, Target>(self, py: Python<'py>, value: Value) -> PyResult<Target>
551 where
552 Value: IntoPyCallbackOutput<'py, Target>,
553 {
554 value.convert(py)
555 }
556}
557
558pub trait AsyncIterBaseKind {
559 #[inline]
560 fn async_iter_tag(&self) -> AsyncIterBaseTag {
561 AsyncIterBaseTag
562 }
563}
564
565impl<Value> AsyncIterBaseKind for &Value {}
566
567pub struct AsyncIterOptionTag;
568
569impl AsyncIterOptionTag {
570 #[inline]
571 pub fn convert<'py, Value>(
572 self,
573 py: Python<'py>,
574 value: Option<Value>,
575 ) -> PyResult<*mut ffi::PyObject>
576 where
577 Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
578 {
579 match value {
580 Some(value) => value.convert(py),
581 None => Err(PyStopAsyncIteration::new_err(())),
582 }
583 }
584}
585
586pub trait AsyncIterOptionKind {
587 #[inline]
588 fn async_iter_tag(&self) -> AsyncIterOptionTag {
589 AsyncIterOptionTag
590 }
591}
592
593impl<Value> AsyncIterOptionKind for Option<Value> {}
594
595pub struct AsyncIterResultOptionTag;
596
597impl AsyncIterResultOptionTag {
598 #[inline]
599 pub fn convert<'py, Value, Error>(
600 self,
601 py: Python<'py>,
602 value: Result<Option<Value>, Error>,
603 ) -> PyResult<*mut ffi::PyObject>
604 where
605 Value: IntoPyCallbackOutput<'py, *mut ffi::PyObject>,
606 Error: Into<PyErr>,
607 {
608 match value {
609 Ok(Some(value)) => value.convert(py),
610 Ok(None) => Err(PyStopAsyncIteration::new_err(())),
611 Err(err) => Err(err.into()),
612 }
613 }
614}
615
616pub trait AsyncIterResultOptionKind {
617 #[inline]
618 fn async_iter_tag(&self) -> AsyncIterResultOptionTag {
619 AsyncIterResultOptionTag
620 }
621}
622
623impl<Value, Error> AsyncIterResultOptionKind for Result<Option<Value>, Error> {}
624
625pub struct BoundRef<'a, 'py, T>(pub &'a Bound<'py, T>);
633
634impl<'a, 'py> BoundRef<'a, 'py, PyAny> {
635 pub unsafe fn ref_from_ptr(py: Python<'py>, ptr: &'a *mut ffi::PyObject) -> Self {
636 BoundRef(Bound::ref_from_ptr(py, ptr))
637 }
638
639 pub unsafe fn ref_from_ptr_or_opt(
640 py: Python<'py>,
641 ptr: &'a *mut ffi::PyObject,
642 ) -> Option<Self> {
643 Bound::ref_from_ptr_or_opt(py, ptr).as_ref().map(BoundRef)
644 }
645
646 pub fn downcast<T: PyTypeCheck>(self) -> Result<BoundRef<'a, 'py, T>, DowncastError<'a, 'py>> {
647 self.0.downcast::<T>().map(BoundRef)
648 }
649
650 pub unsafe fn downcast_unchecked<T>(self) -> BoundRef<'a, 'py, T> {
651 BoundRef(self.0.downcast_unchecked::<T>())
652 }
653}
654
655impl<'a, 'py, T: PyClass> TryFrom<BoundRef<'a, 'py, T>> for PyRef<'py, T> {
656 type Error = PyBorrowError;
657 #[inline]
658 fn try_from(value: BoundRef<'a, 'py, T>) -> Result<Self, Self::Error> {
659 value.0.try_borrow()
660 }
661}
662
663impl<'a, 'py, T: PyClass<Frozen = False>> TryFrom<BoundRef<'a, 'py, T>> for PyRefMut<'py, T> {
664 type Error = PyBorrowMutError;
665 #[inline]
666 fn try_from(value: BoundRef<'a, 'py, T>) -> Result<Self, Self::Error> {
667 value.0.try_borrow_mut()
668 }
669}
670
671impl<'a, 'py, T> From<BoundRef<'a, 'py, T>> for Bound<'py, T> {
672 #[inline]
673 fn from(bound: BoundRef<'a, 'py, T>) -> Self {
674 bound.0.clone()
675 }
676}
677
678impl<'a, 'py, T> From<BoundRef<'a, 'py, T>> for &'a Bound<'py, T> {
679 #[inline]
680 fn from(bound: BoundRef<'a, 'py, T>) -> Self {
681 bound.0
682 }
683}
684
685impl<T> From<BoundRef<'_, '_, T>> for Py<T> {
686 #[inline]
687 fn from(bound: BoundRef<'_, '_, T>) -> Self {
688 bound.0.clone().unbind()
689 }
690}
691
692impl<'py, T> std::ops::Deref for BoundRef<'_, 'py, T> {
693 type Target = Bound<'py, T>;
694 #[inline]
695 fn deref(&self) -> &Self::Target {
696 self.0
697 }
698}
699
700pub unsafe fn tp_new_impl<T: PyClass>(
701 py: Python<'_>,
702 initializer: PyClassInitializer<T>,
703 target_type: *mut ffi::PyTypeObject,
704) -> PyResult<*mut ffi::PyObject> {
705 initializer
706 .create_class_object_of_type(py, target_type)
707 .map(Bound::into_ptr)
708}
709
710#[cfg(test)]
711mod tests {
712 #[test]
713 #[cfg(any(Py_3_10, not(Py_LIMITED_API)))]
714 fn test_fastcall_function_with_keywords() {
715 use super::PyMethodDef;
716 use crate::types::{PyAnyMethods, PyCFunction};
717 use crate::{ffi, Python};
718
719 Python::with_gil(|py| {
720 unsafe extern "C" fn accepts_no_arguments(
721 _slf: *mut ffi::PyObject,
722 _args: *const *mut ffi::PyObject,
723 nargs: ffi::Py_ssize_t,
724 kwargs: *mut ffi::PyObject,
725 ) -> *mut ffi::PyObject {
726 assert_eq!(nargs, 0);
727 assert!(kwargs.is_null());
728 Python::assume_gil_acquired().None().into_ptr()
729 }
730
731 let f = PyCFunction::internal_new(
732 py,
733 &PyMethodDef::fastcall_cfunction_with_keywords(
734 ffi::c_str!("test"),
735 accepts_no_arguments,
736 ffi::c_str!("doc"),
737 ),
738 None,
739 )
740 .unwrap();
741
742 f.call0().unwrap();
743 });
744 }
745}