1use alloc::sync::Arc;
4use core::{
5 future::Future,
6 panic,
7 pin::Pin,
8 task::{Context, Poll, Waker},
9};
10
11use pyo3_macros::{pyclass, pymethods};
12
13use crate::platform::prelude::*;
14use crate::{
15 coroutine::{cancel::ThrowCallback, waker::AsyncioWaker},
16 exceptions::{PyAttributeError, PyRuntimeError, PyStopIteration},
17 panic::PanicException,
18 types::{string::PyStringMethods, PyIterator, PyString},
19 Bound, Py, PyAny, PyErr, PyResult, Python,
20};
21
22pub(crate) mod cancel;
23mod waker;
24
25pub use cancel::CancelHandle;
26
27const COROUTINE_REUSED_ERROR: &str = "cannot reuse already awaited coroutine";
28
29#[pyclass(crate = "crate")]
31pub struct Coroutine {
32 name: Option<Py<PyString>>,
33 qualname_prefix: Option<&'static str>,
34 throw_callback: Option<ThrowCallback>,
35 #[expect(clippy::type_complexity)]
36 future: Option<Pin<Box<dyn Future<Output = PyResult<Py<PyAny>>> + Send>>>,
37 waker: Option<Arc<AsyncioWaker>>,
38}
39
40unsafe impl Sync for Coroutine {}
43
44impl Coroutine {
45 pub(crate) fn new<'py, F>(
52 name: Option<Bound<'py, PyString>>,
53 qualname_prefix: Option<&'static str>,
54 throw_callback: Option<ThrowCallback>,
55 future: F,
56 ) -> Self
57 where
58 F: Future<Output = Result<Py<PyAny>, PyErr>> + Send + 'static,
59 {
60 Self {
61 name: name.map(Bound::unbind),
62 qualname_prefix,
63 throw_callback,
64 future: Some(Box::pin(future)),
65 waker: None,
66 }
67 }
68
69 fn poll(&mut self, py: Python<'_>, throw: Option<Py<PyAny>>) -> PyResult<Py<PyAny>> {
70 let future_rs = match self.future {
72 Some(ref mut fut) => fut,
73 None => return Err(PyRuntimeError::new_err(COROUTINE_REUSED_ERROR)),
74 };
75 match (throw, &self.throw_callback) {
77 (Some(exc), Some(cb)) => cb.throw(exc),
78 (Some(exc), None) => {
79 self.close();
80 return Err(PyErr::from_value(exc.into_bound(py)));
81 }
82 (None, _) => {}
83 }
84 if let Some(waker) = self.waker.as_mut().and_then(Arc::get_mut) {
86 waker.reset();
87 } else {
88 self.waker = Some(Arc::new(AsyncioWaker::new()));
89 }
90 let waker = Waker::from(self.waker.clone().unwrap());
91 let poll = || future_rs.as_mut().poll(&mut Context::from_waker(&waker));
94 match std::panic::catch_unwind(panic::AssertUnwindSafe(poll)) {
95 Ok(Poll::Ready(res)) => {
96 self.close();
97 return Err(PyStopIteration::new_err((res?,)));
98 }
99 Err(err) => {
100 self.close();
101 return Err(PanicException::from_panic_payload(err));
102 }
103 _ => {}
104 }
105 if let Some(future) = self.waker.as_ref().unwrap().initialize_future(py)? {
107 if let Some(future) = PyIterator::from_object(future).unwrap().next() {
110 return Ok(future.unwrap().into());
113 }
114 }
115 Ok(py.None())
118 }
119}
120
121#[pymethods(crate = "crate")]
122impl Coroutine {
123 #[getter]
124 fn __name__(&self, py: Python<'_>) -> PyResult<Py<PyString>> {
125 match &self.name {
126 Some(name) => Ok(name.clone_ref(py)),
127 None => Err(PyAttributeError::new_err("__name__")),
128 }
129 }
130
131 #[getter]
132 fn __qualname__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyString>> {
133 match (&self.name, &self.qualname_prefix) {
134 (Some(name), Some(prefix)) => Ok(PyString::new(
135 py,
136 &format!("{}.{}", prefix, name.bind(py).to_cow()?),
137 )),
138 (Some(name), None) => Ok(name.bind(py).clone()),
139 (None, _) => Err(PyAttributeError::new_err("__qualname__")),
140 }
141 }
142
143 fn send(&mut self, py: Python<'_>, _value: &Bound<'_, PyAny>) -> PyResult<Py<PyAny>> {
144 self.poll(py, None)
145 }
146
147 fn throw(&mut self, py: Python<'_>, exc: Py<PyAny>) -> PyResult<Py<PyAny>> {
148 self.poll(py, Some(exc))
149 }
150
151 fn close(&mut self) {
152 drop(self.future.take());
155 }
156
157 fn __await__(self_: Py<Self>) -> Py<Self> {
158 self_
159 }
160
161 fn __next__(&mut self, py: Python<'_>) -> PyResult<Py<PyAny>> {
162 self.poll(py, None)
163 }
164}