pythonpython-contextvars

python contextvars pass and access a variable along the chain of calls


Context variables are convenient when we need to pass a variable along the chain of calls so that they share the same context, in the case when this cannot be done through a global variable in the case of concurrency. Context variables can be used as an alternative to global variables both in multi-threaded code and in asynchronous (with coroutines).

I can use contextvars in Python 3.7 and above like below and It's usually really easy:

Sample 1:

import contextvars

user_id = contextvars.ContextVar("user_id")

def f1(user, operation):
    user_id.set(user.id)
    f2()

def f2():
    f3()

def f3():
    print(user_id.get())  # gets the user_id value

Sample 2:

But when I am using the contextvars to another module's function it is not accessible, showing below error. It seems I am misunderstanding the usage of contextvars :)

NameError: name 'user_id' is not defined

test2.py

def abc():
    print("inside abc")
    print(user_id.get()) 

if __name__=='__main__':
    abc()

test1.py

import contextvars
from test2 import abc
import uuid
user_id = contextvars.ContextVar("user_id")
request_id = uuid.uuid4()

def f1():
    f2()

def f2():
    f3()

def f3():
    print("inside f3")
    print(user_id.get())  

user_id.set(request_id)
f1_calling = f1()
abc_calling = abc()

Full Output:

inside f3
cdd36594-372d-438a-9bac-da53751af08a
inside abc
Traceback (most recent call last):
  File "/var/www/test1.py", line 19, in <module>
    abc_calling = abc()
  File "/var/www/test2.py", line 3, in abc
    print(user_id.get()) 
NameError: name 'user_id' is not defined

So my fundamental question is how can I pass and access the context variable that I set from one function and access that variable from any sub-function that is called by the main module.?


Solution

  • "Global" variables in Python are not actually global, but are rather attributes of the module that defines them.

    You can therefore access a global variable defined in the main module from a sub-module by accessing it as an attribute of sys.modules['__main__']:

    test2.py

    import sys
    
    def abc():
        print("inside abc")
        print(sys.modules['__main__'].user_id.get())
    

    Demo: https://replit.com/@blhsing/TurquoiseAltruisticPercent#main.py