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.?
"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
Related
I want to refer to an object in the namespace of the file that imports the one that I am writing.
this is an example:
main.py
from imp import * # main is importing the file I'm writing
...more code...
obj=1 # main defines obj
f() # f(), defined in imp, needs to use obj
...more code using obj...
This is the file that defines f():
imp.py
def f():
return obj # I want to refer to main's obj here
error on runtime:
error: global name 'obj' is not defined
How can it be done?
Thanks.
Relying on global variables across modules is not really a good idea. You should pass obj as a parameter to the function f(), like this:
f(obj)
Then just declare the parameter in the function:
def f(obj):
# code to operate on obj
return obj
If I have a python class
from multiprocessing import Process
class A(Process):
def run(self):
self.var = "asdf"
def pprint(self):
print(self.var)
if __name__ == "__main__":
foo = A()
foo.start()
foo.pprint()
bar = A()
bar.pprint()
I get the traceback error
Traceback (most recent call last):
File "simple.py", line 13, in <module>
foo.pprint()
File "simple.py", line 8, in pprint
print(self.var)
AttributeError: 'A' object has no attribute 'var'
Can I access instance variables that are defined within the run function, with other functions defined in the scope of the class?
from multiprocessing import Process
class A(Process):
def __init__(self, value):
self.var = value
def run(self):
self.var = "asdf"
def pprint(self):
print(self.var)
foo = A("asdf")
foo.start()
foo.pprint()
bar = A("qwerty")
bar.pprint()
The run() function is executed in another process and the variable is only created there while pprint() is executed in current process. You can e.g. use a Manager to get a dict to store data common for all processes or exchange data using pipes or queues (read the documentation about that).
Classes B and C both derive from base class A, and neither override A's method test(). B is defined in the same module as A; C is defined in a separate module. How is it that calling B.test() prints "hello", but calling C.test() fails? Shouldn't either invocation end up executing A.test() and therefore be able to resolve the symbol "message" in mod1's namespace?
I'd also gratefully receive hints on where this behaviour is documented as I've been unable to turn up anything. How are names resolved when C.test() is called, and can "message" be injected into one of the namespaces somehow?
FWIW, the reason I haven't used an instance variable (e.g. set A.message = "hello") is because I'm wanting to access a "global" singleton object and don't want to have an explicit referent to it in every other object.
mod1.py:
import mod2
class A(object):
def test(self):
print message
class B(A):
pass
if __name__ == "__main__":
message = "hello"
A().test()
B().test()
mod2.C().test()
mod2.py:
import mod1
class C(mod1.A):
pass
output is:
$ python mod1.py
hello
hello
Traceback (most recent call last):
File "mod1.py", line 14, in <module>
mod2.C().test()
File "mod1.py", line 5, in test
print message
NameError: global name 'message' is not defined
Many thanks!
EOL is correct, moving the "main" part of the program into a new file mod3.py does indeed make things work.
http://bytebaker.com/2008/07/30/python-namespaces/ further clarifies the issue.
In my original question, it turns out that the variable message ist stored in the __main__ module namespace because mod1.py is being run as a script. mod2 imports mod1, but it gets a separate mod1 namespace, where the variable message does not exist. The following code snippet demonstrates more clearly as it writes message into mod1's namespace (not that I'd recommend this be done in real life), causing the expected behaviour.
import sys
class A(object):
def test(self):
print message
class B(A):
pass
if __name__ == "__main__":
import mod2
message = "hello"
sys.modules["mod1"].message = message
A().test()
B().test()
mod2.C().test()
I think the best real-world fix is to move the "main" part of the program into a separate module, as EOL implies, or do:
class A(object):
def test(self):
print message
class B(A):
pass
def main():
global message
message = "hello"
A().test()
B().test()
# resolve circular import by importing in local scope
import mod2
mod2.C().test()
if __name__ == "__main__":
# break into mod1 namespace from __main__ namespace
import mod1
mod1.main()
Could you use a class attribute instead of a global? The following works
import mod2
class A(object):
message = "Hello" # Class attribute (not duplicated in instances)
def test(self):
print self.message # Class A attribute can be overridden by subclasses
class B(A):
pass
if __name__ == "__main__":
A().test()
B().test()
mod2.C().test()
Not using globals is cleaner: in the code above, message is explicitly attached to the class it is used in.
That said, I am also very curious as to why the global message is not found by mod2.C().test().
Things work as expected, though, if the cross-importing is removed (no main program in mod1.py, and no import mod2): importing mod1 and mod2 from mod3.py, doing mod1.message = "Hello" there and mod2.C().test() works. I am therefore wondering if the problem is not related to cross-importing…
This is clearly a scope or import issue of some kind, but I can't figure it out. Something like:
classes.py
class Thing(object):
#property
def global_test(self):
return the_global
And then...
test.py
from classes import Thing
global the_global
the_global = 'foobar'
t = Thing()
t.global_test
:(
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "classes.py", line 4, in global_test
return the_global
NameError: global name 'the_global' is not defined
Any help would be great!
"global" in Python is a variable accessible in top level within module.
This message:
NameError: global name 'the_global' is not defined
raised within classes.py means you do not have a global named the_global within your classes.py file.
Python modules do not share global variables. (well, not in the way you want them to share)
The 'global' variables only defines a variable as global inside the scope of the module
where it is used. You can not use 'global' here to access a variable outside the module
scope of the 'classes' module.
The proper solution here if you have to deal with global defines or so: move the "global"
variables into a dedicated module and use a proper import statement to import the variables
into your 'classes' module.
myvars.py:
MY_GLOBAL_VAR = 42
classes.py:
import myvars
class Thing():
def method(self):
return myvars.MY_GLOBAL_VAR # if you need such a weird pattern for whatever reason
I think what I'm trying to do is fairly simple. I want to initialize a couple of variables in a test setup function, and then use them in the test functions that are decorated with that setup. The following trivial example illustrates what I mean:
from nose.tools import *
def setup():
foo = 10
def teardown():
foo = None
#with_setup(setup, teardown)
def test_foo_value():
assert_equal(foo, 10)
This results in:
$ nosetests tests/test_foo.py
E
======================================================================
ERROR: test_foo.test_foo_value
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/mtozzi/.virtualenvs/foo/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/home/mtozzi/code/foo/tests/test_foo.py", line 12, in test_foo_value
assert_equal(foo, 10)
NameError: global name 'foo' is not defined
----------------------------------------------------------------------
Ran 1 test in 0.006s
FAILED (errors=1)
With the old unittest style, I could set these as instance variables on the test class, but I thought nosetests didn't require you to use classes. I've also considered setting them as package global variables, but that doesn't seem like a terribly good practice. I hope there's something obvious I'm missing for doing this.
As the comments to your question already suggested, simply switch to classes and use instance variables like self.foo. That's the way it should be done.
If you insist on not using classes, try global variables. You didn't hear this from me, though.
from nose.tools import *
foo = None
def setup():
global foo # Ugly.
foo = 10
def teardown():
global foo # Ugly.
foo = None
#with_setup(setup, teardown)
def test_foo_value():
assert_equal(foo, 10)
A third variant might be to use a dictionary for your values. This is slightly less ugly but horribly clumsy:
from nose.tools import *
_globals = {'foo': None}
def setup():
_globals['foo'] = 10
def teardown():
_globals['foo'] = None
#with_setup(setup, teardown)
def test_foo_value():
foo = _globals['foo']
assert_equal(foo, 10)
I use a custom with_setup decorator that uses the poor man's nonlocal: https://gist.github.com/garyvdm/392ae20c673c7ee58d76
def setup():
foo = 10
return [foo], {}
def teardown(foo):
pass
#with_setup_args(setup, teardown)
def test_foo_value(foo):
nose.tools.assert_equal(foo, 10)
For projects that are Python 3 only, I use nonlocal rather than .extend/.update for arguments, kwargs.