| 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 | |