@@ -716,6 +716,34 @@ future_cancel(FutureObj *fut)
716716 Py_RETURN_TRUE ;
717717}
718718
719+ // 7TO6: This method replaces built-in API PyContext_CopyContext
720+ // from Python >= 3.7 by running contextvars.copy_context()
721+ // from a Python module.
722+ static PyObject *
723+ copy_context ()
724+ {
725+ PyObject * empty_tuple = PyTuple_New (0 );
726+
727+ PyObject * contextvars_lib = PyImport_ImportModule ("contextvars" );
728+ if (contextvars_lib == NULL ) {
729+ Py_DECREF (empty_tuple );
730+ return NULL ;
731+ }
732+ PyObject * copy_context_fun = PyObject_GetAttrString (contextvars_lib , "copy_context" );
733+ if (copy_context_fun == NULL ) {
734+ Py_DECREF (empty_tuple );
735+ Py_DECREF (contextvars_lib );
736+ return NULL ;
737+ }
738+ PyObject * context = PyObject_Call (copy_context_fun , empty_tuple , NULL );
739+
740+ Py_DECREF (empty_tuple );
741+ Py_DECREF (copy_context_fun );
742+ Py_DECREF (contextvars_lib );
743+
744+ return context ;
745+ }
746+
719747/*[clinic input]
720748_asyncio.Future.__init__
721749
@@ -911,16 +939,17 @@ _asyncio_Future_add_done_callback_impl(FutureObj *self, PyObject *fn,
911939 PyObject * context )
912940/*[clinic end generated code: output=7ce635bbc9554c1e input=15ab0693a96e9533]*/
913941{
914- // 7TO6: Ignore context as is implemented in pure Python
915- /* if (context == NULL) {
916- context = PyContext_CopyCurrent();
942+ if (context == NULL ) {
943+ // 7TO6: Use pure-Python version of copy_context
944+ /* context = PyContext_CopyCurrent(); */
945+ context = copy_context ();
917946 if (context == NULL ) {
918947 return NULL ;
919948 }
920949 PyObject * res = future_add_done_callback (self , fn , context );
921950 Py_DECREF (context );
922951 return res ;
923- } */
952+ }
924953 return future_add_done_callback (self , fn , context );
925954}
926955
@@ -1994,11 +2023,12 @@ _asyncio_Task___init___impl(TaskObj *self, PyObject *coro, PyObject *loop)
19942023 return -1 ;
19952024 }
19962025
1997- // 7TO6: Ignore context as is implemented in pure Python
1998- /* Py_XSETREF(self->task_context, PyContext_CopyCurrent());
2026+ // 7TO6: Use pure-Python version of copy_context
2027+ /* Py_XSETREF(self->task_context, PyContext_CopyCurrent()); */
2028+ Py_XSETREF (self -> task_context , copy_context ());
19992029 if (self -> task_context == NULL ) {
20002030 return -1 ;
2001- } */
2031+ }
20022032
20032033 Py_CLEAR (self -> task_fut_waiter );
20042034 self -> task_must_cancel = 0 ;
@@ -2091,6 +2121,18 @@ TaskObj_get_fut_waiter(TaskObj *task, void *Py_UNUSED(ignored))
20912121 Py_RETURN_NONE ;
20922122}
20932123
2124+ // 7TO6: Expose task context to Python modules
2125+ static PyObject *
2126+ TaskObj_get_task_context (TaskObj * task , void * Py_UNUSED (ignored ))
2127+ {
2128+ if (task -> task_context ) {
2129+ Py_INCREF (task -> task_context );
2130+ return task -> task_context ;
2131+ }
2132+
2133+ Py_RETURN_NONE ;
2134+ }
2135+
20942136/*[clinic input]
20952137@classmethod
20962138_asyncio.Task.current_task
@@ -2433,6 +2475,7 @@ static PyGetSetDef TaskType_getsetlist[] = {
24332475 {"_must_cancel" , (getter )TaskObj_get_must_cancel , NULL , NULL },
24342476 {"_coro" , (getter )TaskObj_get_coro , NULL , NULL },
24352477 {"_fut_waiter" , (getter )TaskObj_get_fut_waiter , NULL , NULL },
2478+ {"_task_context" , (getter )TaskObj_get_task_context , NULL , NULL }, // 7TO6: Expose _task_context
24362479 {NULL } /* Sentinel */
24372480};
24382481
0 commit comments