@@ -1095,6 +1095,111 @@ where
1095
1095
Ok ( future_rx)
1096
1096
}
1097
1097
1098
+ /// Convert a `!Send` Rust Future into a Python awaitable with a generic runtime
1099
+ ///
1100
+ /// Unlike [`local_future_into_py_with_loop`], this function will stop the Rust future from running
1101
+ /// when the `asyncio.Future` is cancelled from Python.
1102
+ ///
1103
+ /// # Arguments
1104
+ /// * `event_loop` - The Python event loop that the awaitable should be attached to
1105
+ /// * `fut` - The Rust future to be converted
1106
+ ///
1107
+ /// # Examples
1108
+ ///
1109
+ /// ```no_run
1110
+ /// # use std::{task::{Context, Poll}, pin::Pin, future::Future};
1111
+ /// #
1112
+ /// # use pyo3_asyncio::generic::{JoinError, Runtime};
1113
+ /// #
1114
+ /// # struct MyCustomJoinError;
1115
+ /// #
1116
+ /// # impl JoinError for MyCustomJoinError {
1117
+ /// # fn is_panic(&self) -> bool {
1118
+ /// # unreachable!()
1119
+ /// # }
1120
+ /// # }
1121
+ /// #
1122
+ /// # struct MyCustomJoinHandle;
1123
+ /// #
1124
+ /// # impl Future for MyCustomJoinHandle {
1125
+ /// # type Output = Result<(), MyCustomJoinError>;
1126
+ /// #
1127
+ /// # fn poll(self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
1128
+ /// # unreachable!()
1129
+ /// # }
1130
+ /// # }
1131
+ /// #
1132
+ /// # struct MyCustomRuntime;
1133
+ /// #
1134
+ /// # impl MyCustomRuntime {
1135
+ /// # async fn sleep(_: Duration) {
1136
+ /// # unreachable!()
1137
+ /// # }
1138
+ /// # }
1139
+ /// #
1140
+ /// # impl Runtime for MyCustomRuntime {
1141
+ /// # type JoinError = MyCustomJoinError;
1142
+ /// # type JoinHandle = MyCustomJoinHandle;
1143
+ /// #
1144
+ /// # fn scope<F, R>(_event_loop: PyObject, fut: F) -> Pin<Box<dyn Future<Output = R> + Send>>
1145
+ /// # where
1146
+ /// # F: Future<Output = R> + Send + 'static
1147
+ /// # {
1148
+ /// # unreachable!()
1149
+ /// # }
1150
+ /// # fn get_task_event_loop(py: Python) -> Option<&PyAny> {
1151
+ /// # unreachable!()
1152
+ /// # }
1153
+ /// #
1154
+ /// # fn spawn<F>(fut: F) -> Self::JoinHandle
1155
+ /// # where
1156
+ /// # F: Future<Output = ()> + Send + 'static
1157
+ /// # {
1158
+ /// # unreachable!()
1159
+ /// # }
1160
+ /// # }
1161
+ /// #
1162
+ /// use std::time::Duration;
1163
+ ///
1164
+ /// use pyo3::prelude::*;
1165
+ ///
1166
+ /// /// Awaitable sleep function
1167
+ /// #[pyfunction]
1168
+ /// fn sleep_for<'p>(py: Python<'p>, secs: &'p PyAny) -> PyResult<&'p PyAny> {
1169
+ /// // Rc is !Send so it cannot be passed into pyo3_asyncio::generic::future_into_py
1170
+ /// let secs = Rc::new(secs);
1171
+ ///
1172
+ /// pyo3_asyncio::generic::local_cancellable_future_into_py_with_loop::<MyCustomRuntime, _>(
1173
+ /// pyo3_asyncio::get_running_loop(py)?,
1174
+ /// async move {
1175
+ /// MyCustomRuntime::sleep(Duration::from_secs(*secs)).await;
1176
+ /// Python::with_gil(|py| Ok(py.None()))
1177
+ /// }
1178
+ /// )
1179
+ /// }
1180
+ /// ```
1181
+ pub fn local_cancellable_future_into_py_with_loop < R , F > ( event_loop : & PyAny , fut : F ) -> PyResult < & PyAny >
1182
+ where
1183
+ R : Runtime + SpawnLocalExt ,
1184
+ F : Future < Output = PyResult < PyObject > > + ' static ,
1185
+ {
1186
+ let ( cancel_tx, cancel_rx) = oneshot:: channel ( ) ;
1187
+
1188
+ let py_fut = local_future_into_py_with_loop :: < R , _ > (
1189
+ event_loop,
1190
+ Cancellable :: new_with_cancel_rx ( fut, cancel_rx) ,
1191
+ ) ?;
1192
+
1193
+ py_fut. call_method1 (
1194
+ "add_done_callback" ,
1195
+ ( PyDoneCallback {
1196
+ cancel_tx : Some ( cancel_tx) ,
1197
+ } , ) ,
1198
+ ) ?;
1199
+
1200
+ Ok ( py_fut)
1201
+ }
1202
+
1098
1203
/// Convert a `!Send` Rust Future into a Python awaitable with a generic runtime
1099
1204
///
1100
1205
/// # Arguments
0 commit comments