Skip to content

Commit a039895

Browse files
author
Andrew J Westlake
committed
Added docs for cancellation and contextvars behaviour as well as a workaround for synchronous functions
1 parent 0243336 commit a039895

File tree

3 files changed

+188
-4
lines changed

3 files changed

+188
-4
lines changed

src/async_std.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -251,8 +251,24 @@ where
251251

252252
/// Convert a Rust Future into a Python awaitable
253253
///
254+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
255+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
256+
///
257+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
258+
/// via [`into_future`] (new behaviour in `v0.15`).
259+
///
260+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
261+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
262+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
263+
/// >
264+
/// > As a workaround, you can get the `contextvars` from the current task locals using
265+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
266+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
267+
/// synchronous function, and restore the previous context when it returns or raises an exception.
268+
///
254269
/// # Arguments
255-
/// * `event_loop` - The Python event loop that the awaitable should be attached to
270+
/// * `py` - PyO3 GIL guard
271+
/// * `locals` - The task locals for the given future
256272
/// * `fut` - The Rust future to be converted
257273
///
258274
/// # Examples
@@ -330,6 +346,21 @@ where
330346

331347
/// Convert a Rust Future into a Python awaitable
332348
///
349+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
350+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
351+
///
352+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
353+
/// via [`into_future`] (new behaviour in `v0.15`).
354+
///
355+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
356+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
357+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
358+
/// >
359+
/// > As a workaround, you can get the `contextvars` from the current task locals using
360+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
361+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
362+
/// synchronous function, and restore the previous context when it returns or raises an exception.
363+
///
333364
/// # Arguments
334365
/// * `py` - The current PyO3 GIL guard
335366
/// * `fut` - The Rust future to be converted
@@ -457,8 +488,24 @@ where
457488

458489
/// Convert a `!Send` Rust Future into a Python awaitable
459490
///
491+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
492+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
493+
///
494+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
495+
/// via [`into_future`] (new behaviour in `v0.15`).
496+
///
497+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
498+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
499+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
500+
/// >
501+
/// > As a workaround, you can get the `contextvars` from the current task locals using
502+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
503+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
504+
/// synchronous function, and restore the previous context when it returns or raises an exception.
505+
///
460506
/// # Arguments
461-
/// * `event_loop` - The Python event loop that the awaitable should be attached to
507+
/// * `py` - PyO3 GIL guard
508+
/// * `locals` - The task locals for the given future
462509
/// * `fut` - The Rust future to be converted
463510
///
464511
/// # Examples
@@ -570,6 +617,21 @@ where
570617

571618
/// Convert a `!Send` Rust Future into a Python awaitable
572619
///
620+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
621+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
622+
///
623+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
624+
/// via [`into_future`] (new behaviour in `v0.15`).
625+
///
626+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
627+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
628+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
629+
/// >
630+
/// > As a workaround, you can get the `contextvars` from the current task locals using
631+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
632+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
633+
/// synchronous function, and restore the previous context when it returns or raises an exception.
634+
///
573635
/// # Arguments
574636
/// * `py` - The current PyO3 GIL guard
575637
/// * `fut` - The Rust future to be converted

src/generic.rs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,21 @@ where
421421

422422
/// Convert a Rust Future into a Python awaitable with a generic runtime
423423
///
424+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
425+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
426+
///
427+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
428+
/// via [`into_future`] (new behaviour in `v0.15`).
429+
///
430+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
431+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
432+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
433+
/// >
434+
/// > As a workaround, you can get the `contextvars` from the current task locals using
435+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
436+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
437+
/// synchronous function, and restore the previous context when it returns or raises an exception.
438+
///
424439
/// # Arguments
425440
/// * `py` - PyO3 GIL guard
426441
/// * `locals` - The task-local data for Python
@@ -867,6 +882,21 @@ where
867882

868883
/// Convert a Rust Future into a Python awaitable with a generic runtime
869884
///
885+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
886+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
887+
///
888+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
889+
/// via [`into_future`] (new behaviour in `v0.15`).
890+
///
891+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
892+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
893+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
894+
/// >
895+
/// > As a workaround, you can get the `contextvars` from the current task locals using
896+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
897+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
898+
/// synchronous function, and restore the previous context when it returns or raises an exception.
899+
///
870900
/// # Arguments
871901
/// * `py` - The current PyO3 GIL guard
872902
/// * `fut` - The Rust future to be converted
@@ -1055,6 +1085,21 @@ where
10551085
/// Convert a `!Send` Rust Future into a Python awaitable with a generic runtime and manual
10561086
/// specification of task locals.
10571087
///
1088+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
1089+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
1090+
///
1091+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
1092+
/// via [`into_future`] (new behaviour in `v0.15`).
1093+
///
1094+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
1095+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
1096+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
1097+
/// >
1098+
/// > As a workaround, you can get the `contextvars` from the current task locals using
1099+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
1100+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
1101+
/// synchronous function, and restore the previous context when it returns or raises an exception.
1102+
///
10581103
/// # Arguments
10591104
/// * `py` - PyO3 GIL guard
10601105
/// * `locals` - The task locals for the future
@@ -1458,6 +1503,21 @@ where
14581503

14591504
/// Convert a `!Send` Rust Future into a Python awaitable with a generic runtime
14601505
///
1506+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
1507+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
1508+
///
1509+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
1510+
/// via [`into_future`] (new behaviour in `v0.15`).
1511+
///
1512+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
1513+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
1514+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
1515+
/// >
1516+
/// > As a workaround, you can get the `contextvars` from the current task locals using
1517+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
1518+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
1519+
/// synchronous function, and restore the previous context when it returns or raises an exception.
1520+
///
14611521
/// # Arguments
14621522
/// * `py` - The current PyO3 GIL guard
14631523
/// * `fut` - The Rust future to be converted

src/tokio.rs

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -268,8 +268,24 @@ where
268268

269269
/// Convert a Rust Future into a Python awaitable
270270
///
271+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
272+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
273+
///
274+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
275+
/// via [`into_future`] (new behaviour in `v0.15`).
276+
///
277+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
278+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
279+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
280+
/// >
281+
/// > As a workaround, you can get the `contextvars` from the current task locals using
282+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
283+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
284+
/// synchronous function, and restore the previous context when it returns or raises an exception.
285+
///
271286
/// # Arguments
272-
/// * `event_loop` - The Python event loop that the awaitable should be attached to
287+
/// * `py` - PyO3 GIL guard
288+
/// * `locals` - The task locals for the given future
273289
/// * `fut` - The Rust future to be converted
274290
///
275291
/// # Examples
@@ -347,6 +363,21 @@ where
347363

348364
/// Convert a Rust Future into a Python awaitable
349365
///
366+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
367+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
368+
///
369+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
370+
/// via [`into_future`] (new behaviour in `v0.15`).
371+
///
372+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
373+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
374+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
375+
/// >
376+
/// > As a workaround, you can get the `contextvars` from the current task locals using
377+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
378+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
379+
/// synchronous function, and restore the previous context when it returns or raises an exception.
380+
///
350381
/// # Arguments
351382
/// * `py` - The current PyO3 GIL guard
352383
/// * `fut` - The Rust future to be converted
@@ -490,8 +521,24 @@ where
490521

491522
/// Convert a `!Send` Rust Future into a Python awaitable
492523
///
524+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
525+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
526+
///
527+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
528+
/// via [`into_future`] (new behaviour in `v0.15`).
529+
///
530+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
531+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
532+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
533+
/// >
534+
/// > As a workaround, you can get the `contextvars` from the current task locals using
535+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
536+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
537+
/// synchronous function, and restore the previous context when it returns or raises an exception.
538+
///
493539
/// # Arguments
494-
/// * `event_loop` - The Python event loop that the awaitable should be attached to
540+
/// * `py` - PyO3 GIL guard
541+
/// * `locals` - The task locals for the given future
495542
/// * `fut` - The Rust future to be converted
496543
///
497544
/// # Examples
@@ -638,6 +685,21 @@ where
638685

639686
/// Convert a `!Send` Rust Future into a Python awaitable
640687
///
688+
/// If the `asyncio.Future` returned by this conversion is cancelled via `asyncio.Future.cancel`,
689+
/// the Rust future will be cancelled as well (new behaviour in `v0.15`).
690+
///
691+
/// Python `contextvars` are preserved when calling async Python functions within the Rust future
692+
/// via [`into_future`] (new behaviour in `v0.15`).
693+
///
694+
/// > Although `contextvars` are preserved for async Python functions, synchronous functions will
695+
/// unfortunately fail to resolve them when called within the Rust future. This is because the
696+
/// function is being called from a Rust thread, not inside an actual Python coroutine context.
697+
/// >
698+
/// > As a workaround, you can get the `contextvars` from the current task locals using
699+
/// [`get_current_locals`] and [`TaskLocals::context`](`crate::TaskLocals::context`), then wrap your
700+
/// synchronous function in a call to `contextvars.Context.run`. This will set the context, call the
701+
/// synchronous function, and restore the previous context when it returns or raises an exception.
702+
///
641703
/// # Arguments
642704
/// * `py` - The current PyO3 GIL guard
643705
/// * `fut` - The Rust future to be converted

0 commit comments

Comments
 (0)