Skip to content

Commit 268de73

Browse files
author
Andrew J Westlake
committed
Added Python 3.6 fallbacks for contextvars
1 parent 67cea73 commit 268de73

File tree

3 files changed

+35
-8
lines changed

3 files changed

+35
-8
lines changed

pytests/test_async_std_asyncio.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,9 +347,15 @@ asyncio.run(main())
347347
#[pyo3_asyncio::async_std::test]
348348
fn test_contextvars() -> PyResult<()> {
349349
Python::with_gil(|py| {
350+
let contextvars = match py.import("contextvars") {
351+
Ok(contextvars) => contextvars,
352+
// Python 3.6 does not support contextvars, so early-out here
353+
Err(_) => return Ok(()),
354+
};
355+
350356
let d = [
351357
("asyncio", py.import("asyncio")?.into()),
352-
("contextvars", py.import("contextvars")?.into()),
358+
("contextvars", contextvars.into()),
353359
("cvars_mod", wrap_pymodule!(cvars_mod)(py)),
354360
]
355361
.into_py_dict(py);

pytests/tokio_asyncio/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,15 @@ asyncio.run(main())
345345
#[pyo3_asyncio::async_std::test]
346346
fn test_contextvars() -> PyResult<()> {
347347
Python::with_gil(|py| {
348+
let contextvars = match py.import("contextvars") {
349+
Ok(contextvars) => contextvars,
350+
// Python 3.6 does not support contextvars, so early-out here
351+
Err(_) => return Ok(()),
352+
};
353+
348354
let d = [
349355
("asyncio", py.import("asyncio")?.into()),
350-
("contextvars", py.import("contextvars")?.into()),
356+
("contextvars", contextvars.into()),
351357
("cvars_mod", wrap_pymodule!(cvars_mod)(py)),
352358
]
353359
.into_py_dict(py);

src/lib.rs

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ use pyo3::{
353353
};
354354

355355
static ASYNCIO: OnceCell<PyObject> = OnceCell::new();
356-
static CONTEXTVARS: OnceCell<PyObject> = OnceCell::new();
356+
static CONTEXTVARS: OnceCell<Option<PyObject>> = OnceCell::new();
357357
static ENSURE_FUTURE: OnceCell<PyObject> = OnceCell::new();
358358
static GET_RUNNING_LOOP: OnceCell<PyObject> = OnceCell::new();
359359

@@ -500,14 +500,24 @@ pub fn get_running_loop(py: Python) -> PyResult<&PyAny> {
500500
.call0()
501501
}
502502

503-
fn contextvars(py: Python) -> PyResult<&PyAny> {
503+
/// Returns None only if contextvars cannot be imported (Python 3.6 fallback)
504+
fn contextvars(py: Python) -> Option<&PyAny> {
504505
CONTEXTVARS
505-
.get_or_try_init(|| Ok(py.import("contextvars")?.into()))
506+
.get_or_init(|| match py.import("contextvars") {
507+
Ok(contextvars) => Some(contextvars.into()),
508+
Err(_) => None,
509+
})
510+
.as_ref()
506511
.map(|contextvars| contextvars.as_ref(py))
507512
}
508513

509-
fn copy_context(py: Python) -> PyResult<&PyAny> {
510-
contextvars(py)?.call_method0("copy_context")
514+
/// Returns Ok(None) only if contextvars cannot be imported (Python 3.6 fallback)
515+
fn copy_context(py: Python) -> PyResult<Option<&PyAny>> {
516+
if let Some(contextvars) = contextvars(py) {
517+
Ok(Some(contextvars.call_method0("copy_context")?))
518+
} else {
519+
Ok(None)
520+
}
511521
}
512522

513523
/// Task-local data to store for Python conversions.
@@ -539,7 +549,12 @@ impl TaskLocals {
539549

540550
/// Capture the current task's contextvars
541551
pub fn copy_context(self, py: Python) -> PyResult<Self> {
542-
Ok(self.context(copy_context(py)?))
552+
// No-op if context cannot be copied (Python 3.6 fallback)
553+
if let Some(cx) = copy_context(py)? {
554+
Ok(self.context(cx))
555+
} else {
556+
Ok(self)
557+
}
543558
}
544559
}
545560

0 commit comments

Comments
 (0)