@@ -180,6 +180,50 @@ pub fn get_event_loop() -> PyObject {
180
180
}
181
181
182
182
/// Run the event loop forever
183
+ ///
184
+ /// This can be called instead of [`run_until_complete`] to run the event loop
185
+ /// until `stop` is called rather than driving a future to completion.
186
+ ///
187
+ /// After this function returns, the event loop can be resumed with either [`run_until_complete`] or
188
+ /// [`run_forever`]
189
+ ///
190
+ /// # Arguments
191
+ /// * `py` - The current PyO3 GIL guard
192
+ ///
193
+ /// # Examples
194
+ ///
195
+ /// ```no_run
196
+ /// # use std::time::Duration;
197
+ /// # use pyo3::prelude::*;
198
+ /// # Python::with_gil(|py| {
199
+ /// # pyo3_asyncio::with_runtime(py, || {
200
+ /// // Wait 1 second, then stop the event loop
201
+ /// pyo3_asyncio::spawn(async move {
202
+ /// tokio::time::sleep(Duration::from_secs(1)).await;
203
+ /// Python::with_gil(|py| {
204
+ /// let event_loop = pyo3_asyncio::get_event_loop();
205
+ ///
206
+ /// event_loop
207
+ /// .call_method1(
208
+ /// py,
209
+ /// "call_soon_threadsafe",
210
+ /// (event_loop
211
+ /// .getattr(py, "stop")
212
+ /// .map_err(|e| e.print_and_set_sys_last_vars(py))
213
+ /// .unwrap(),),
214
+ /// )
215
+ /// .map_err(|e| e.print_and_set_sys_last_vars(py))
216
+ /// .unwrap();
217
+ /// })
218
+ /// });
219
+ ///
220
+ /// // block until stop is called
221
+ /// pyo3_asyncio::run_forever(py)?;
222
+ /// # Ok(())
223
+ /// # })
224
+ /// # .map_err(|e| e.print_and_set_sys_last_vars(py))
225
+ /// # .unwrap();
226
+ /// # })
183
227
pub fn run_forever ( py : Python ) -> PyResult < ( ) > {
184
228
if let Err ( e) = EVENT_LOOP . get ( ) . unwrap ( ) . call_method0 ( py, "run_forever" ) {
185
229
if e. is_instance :: < PyKeyboardInterrupt > ( py) {
@@ -193,6 +237,37 @@ pub fn run_forever(py: Python) -> PyResult<()> {
193
237
}
194
238
195
239
/// Run the event loop until the given Future completes
240
+ ///
241
+ /// The event loop runs until the given future is complete.
242
+ ///
243
+ /// After this function returns, the event loop can be resumed with either [`run_until_complete`] or
244
+ /// [`run_forever`]
245
+ ///
246
+ /// # Arguments
247
+ /// * `py` - The current PyO3 GIL guard
248
+ /// * `fut` - The future to drive to completion
249
+ ///
250
+ /// # Examples
251
+ ///
252
+ /// ```no_run
253
+ /// # use std::time::Duration;
254
+ /// # use pyo3::prelude::*;
255
+ /// #
256
+ /// # Python::with_gil(|py| {
257
+ /// # pyo3_asyncio::with_runtime(py, || {
258
+ /// pyo3_asyncio::run_until_complete(py, async move {
259
+ /// tokio::time::sleep(Duration::from_secs(1)).await;
260
+ /// Ok(())
261
+ /// })?;
262
+ /// # Ok(())
263
+ /// # })
264
+ /// # .map_err(|e| {
265
+ /// # e.print_and_set_sys_last_vars(py);
266
+ /// # })
267
+ /// # .unwrap();
268
+ /// # });
269
+ /// ```
270
+ ///
196
271
pub fn run_until_complete < F > ( py : Python , fut : F ) -> PyResult < ( ) >
197
272
where
198
273
F : Future < Output = PyResult < ( ) > > + Send + ' static ,
@@ -220,7 +295,40 @@ fn try_close(py: Python) -> PyResult<()> {
220
295
Ok ( ( ) )
221
296
}
222
297
223
- /// Spawn a Future onto the executor
298
+ /// Spawn a Future onto the Rust executor
299
+ ///
300
+ /// This method should be used in place of [`tokio::spawn`] when it is called on a thread that is not
301
+ /// owned by the `tokio` runtime. [`tokio::spawn`] should still work fine from inside a task on the
302
+ /// event loop as the current event loop is stored in Thread-Local storage.
303
+ ///
304
+ /// # Arguments
305
+ /// * `fut` - The future to spawn on the Rust event loop
306
+ ///
307
+ /// # Examples
308
+ ///
309
+ /// ```no_run
310
+ /// # use std::time::Duration;
311
+ /// # use pyo3::prelude::*;
312
+ /// #
313
+ /// # Python::with_gil(|py| {
314
+ /// # pyo3_asyncio::with_runtime(py, || {
315
+ /// # pyo3_asyncio::run_until_complete(py, async move {
316
+ /// #
317
+ /// pyo3_asyncio::spawn(async move {
318
+ /// tokio::time::sleep(Duration::from_secs(1)).await;
319
+ /// })
320
+ /// .await;
321
+ /// #
322
+ /// # Ok(())
323
+ /// # })?;
324
+ /// # Ok(())
325
+ /// # })
326
+ /// # .map_err(|e| {
327
+ /// # e.print_and_set_sys_last_vars(py);
328
+ /// # })
329
+ /// # .unwrap();
330
+ /// # });
331
+ /// ```
224
332
pub fn spawn < F > ( fut : F ) -> JoinHandle < F :: Output >
225
333
where
226
334
F : Future + Send + ' static ,
@@ -229,7 +337,41 @@ where
229
337
CURRENT_THREAD_RUNTIME . spawn ( fut)
230
338
}
231
339
232
- /// Spawn a blocking task onto the executor
340
+ /// Spawn a blocking task onto Rust the executor
341
+ ///
342
+ /// This method should be used in place of [`tokio::task::spawn_blocking`] when it is called on a
343
+ /// thread that is not owned by the `tokio` runtime. [`tokio::task::spawn_blocking`] should still
344
+ /// work fine from inside a task on the event loop as the current event loop is stored in
345
+ /// Thread-Local storage.
346
+ ///
347
+ /// # Arguments
348
+ /// * `func` - The blocking task to spawn on the Rust event loop
349
+ ///
350
+ /// # Examples
351
+ ///
352
+ /// ```no_run
353
+ /// # use std::time::Duration;
354
+ /// # use pyo3::prelude::*;
355
+ /// #
356
+ /// # Python::with_gil(|py| {
357
+ /// # pyo3_asyncio::with_runtime(py, || {
358
+ /// # pyo3_asyncio::run_until_complete(py, async move {
359
+ /// #
360
+ /// pyo3_asyncio::spawn_blocking(|| {
361
+ /// std::thread::sleep(Duration::from_secs(1))
362
+ /// })
363
+ /// .await;
364
+ /// #
365
+ /// # Ok(())
366
+ /// # })?;
367
+ /// # Ok(())
368
+ /// # })
369
+ /// # .map_err(|e| {
370
+ /// # e.print_and_set_sys_last_vars(py);
371
+ /// # })
372
+ /// # .unwrap();
373
+ /// # });
374
+ /// ```
233
375
pub fn spawn_blocking < F , R > ( func : F ) -> JoinHandle < R >
234
376
where
235
377
F : FnOnce ( ) -> R + Send + ' static ,
@@ -264,6 +406,50 @@ impl PyTaskCompleter {
264
406
}
265
407
266
408
/// Convert a Python coroutine into a Rust Future
409
+ ///
410
+ /// # Arguments
411
+ /// * `py` - The current PyO3 GIL guard
412
+ /// * `coro` - The Python coroutine to be converted
413
+ ///
414
+ /// # Examples
415
+ ///
416
+ /// ```no_run
417
+ /// use std::time::Duration;
418
+ ///
419
+ /// use pyo3::prelude::*;
420
+ ///
421
+ /// const PYTHON_CODE: &'static str = r#"
422
+ /// import asyncio
423
+ ///
424
+ /// async def py_sleep(duration):
425
+ /// await asyncio.sleep(duration)
426
+ /// "#;
427
+ ///
428
+ /// async fn py_sleep(seconds: f32) -> PyResult<()> {
429
+ /// let test_mod = Python::with_gil(|py| -> PyResult<PyObject> {
430
+ /// Ok(
431
+ /// PyModule::from_code(
432
+ /// py,
433
+ /// PYTHON_CODE,
434
+ /// "test_into_future/test_mod.py",
435
+ /// "test_mod"
436
+ /// )?
437
+ /// .into()
438
+ /// )
439
+ /// })?;
440
+ ///
441
+ /// Python::with_gil(|py| {
442
+ /// pyo3_asyncio::into_future(
443
+ /// py,
444
+ /// test_mod
445
+ /// .call_method1(py, "py_sleep", (seconds.into_py(py),))?
446
+ /// .as_ref(py),
447
+ /// )
448
+ /// })?
449
+ /// .await?;
450
+ /// Ok(())
451
+ /// }
452
+ /// ```
267
453
pub fn into_future (
268
454
py : Python ,
269
455
coro : & PyAny ,
@@ -305,6 +491,29 @@ fn dump_err(py: Python<'_>) -> impl FnOnce(PyErr) + '_ {
305
491
}
306
492
307
493
/// Convert a Rust Future into a Python coroutine
494
+ ///
495
+ /// # Arguments
496
+ /// * `py` - The current PyO3 GIL guard
497
+ /// * `fut` - The Rust future to be converted
498
+ ///
499
+ /// # Examples
500
+ ///
501
+ /// ```no_run
502
+ /// use std::time::Duration;
503
+ ///
504
+ /// use pyo3::prelude::*;
505
+ ///
506
+ /// /// Awaitable sleep function
507
+ /// #[pyfunction]
508
+ /// fn sleep_for(py: Python, secs: &PyAny) -> PyResult<PyObject> {
509
+ /// let secs = secs.extract()?;
510
+ ///
511
+ /// pyo3_asyncio::into_coroutine(py, async move {
512
+ /// tokio::time::sleep(Duration::from_secs(secs)).await;
513
+ /// Python::with_gil(|py| Ok(py.None()))
514
+ /// })
515
+ /// }
516
+ /// ```
308
517
pub fn into_coroutine < F > ( py : Python , fut : F ) -> PyResult < PyObject >
309
518
where
310
519
F : Future < Output = PyResult < PyObject > > + Send + ' static ,
0 commit comments