3

First, note that it is not like other SO questions about variables inside an exec. here it is a problem for a variable used in a list comprehension TEST within an exec.

Take this test.py :

myglob_var = 'my global var'

def myfunc():
    s = """
print('myglob_var =',myglob_var)
users = ['root','service']
flag = True
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    exec(s)

myfunc()

When executing I have this :

$ python3 test.py
myglob_var = my global var
list comprehension = ['root', 'service']
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    myfunc()
  File "test.py", line 15, in myfunc
    exec(s)
  File "<string>", line 7, in <module>
  File "<string>", line 7, in <listcomp>
NameError: name 'flag' is not defined

both users and flag are defined inside the exec(). both are used in list comprehension. but only flag is seen as undefined because it is used inside a test.

I can workaround that by using exec(s,globals()) :

myglob_var = 'my global var'

def myfunc():
    s = """
print('myglob_var =',myglob_var)
users = ['root','service']
flag = True
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    exec(s, globals())
    print('some_result as global var =',globals().get('some_result'))
    print('some_result as local var =',locals().get('some_result'))

myfunc()

when executing I get :

$ python3 test.py
myglob_var = my global var
list comprehension = ['root', 'service']
list comprehension with test = ['root', 'service']
some_result as global var = a result
some_result as local var = None

Everything is fine except I want the some_result to be local and not global.

to do so, I used a recipe from another question on SO :

myglob_var = 'my global var'

def myfunc():
    s = """
print('myglob_var =',myglob_var)
users = ['root','service']
flag = True
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    nm = {}
    exec(s, globals(), nm)
    print('Result =',nm.get('some_result'))

myfunc()

but the undefined on flag appear again :

$ python3 test.py
myglob_var = my global var
list comprehension = ['root', 'service']
Traceback (most recent call last):
  File "test.py", line 18, in <module>
    myfunc()
  File "test.py", line 15, in myfunc
    exec(s, globals(), nm)
  File "<string>", line 7, in <module>
  File "<string>", line 7, in <listcomp>
NameError: name 'flag' is not defined

EDIT :

I can workaround like this :

myglob_var = 'my global var'

def myfunc():
    s = """
print('myglob_var =',myglob_var)
users = ['root','service']
flag = True
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    nm = globals().copy()
    exec(s, nm)
    print('Result =',nm.get('some_result'))

myfunc()

I get this :

myglob_var = my global var
list comprehension = ['root', 'service']
list comprehension with test = ['root', 'service']
Result = a result

It looks fine except that in my real application, variable assignments are before the exec :

myglob_var = 'my global var'

def myfunc():
    flag = True
    users = ['root','service']
    s = """
print('myglob_var =',myglob_var)
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    nm = globals().copy()
    exec(s, nm, locals())
    print('Result =',nm.get('some_result'))

myfunc()

And this time, it triggers again the same problem : users is defined but not flag inside the exec :

$ python3 test.py
myglob_var = my global var
list comprehension = ['root', 'service']
Traceback (most recent call last):
  File "test.py", line 19, in <module>
    myfunc()
  File "test.py", line 15, in myfunc
    exec(s, nm, locals())
  File "<string>", line 5, in <module>
  File "<string>", line 5, in <listcomp>
NameError: name 'flag' is not defined

I would like inside the exec : use global variables, pass function local variables, be able to return a result locally AND use a variable in a list comprehension test. I cannot see a solution yet : Do you have an idea ?

Eric
  • 4,821
  • 6
  • 33
  • 60

2 Answers2

2
def myfunc():
    s = """
users = ['root','service']
flag = True
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    nm = {}
    exec(s, nm)
    print('Result =', nm.get('some_result'))

myfunc()

list comprehension = ['root', 'service']

list comprehension with test = ['root', 'service']

Result = a result

The exec function may be applied just with one namespace dictionary. Make nm variable your namespace for local variables.

Update:

myglob_var = 'my global var'

def myfunc():
    s = """
print('myglob_var =',myglob_var)
users = ['root','service']
flag = True
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""

    nm = {"myglob_var": myglob_var}
    exec(s, nm)
    print('Result =', nm.get("some_result"))

myfunc()

Is it suitable to you define the variables that you really need in namespace?

Update 2:

    nm = globals().copy()
    nm.update(locals())
    exec(s, nm)

How about pass your local variables to the namespace?

Community
  • 1
  • 1
MartenCatcher
  • 2,713
  • 8
  • 26
  • 39
  • it does not work because it does not pass global vars : in my example : NameError: name 'myglob_var' is not defined – Eric Jun 20 '19 at 11:12
  • @Eric sorry, I haven't payed enough attention to your question. I've updated my answer. Is it suitable to you? – MartenCatcher Jun 20 '19 at 11:29
  • I edited my question : it is more complicated than that : please could have a look ? – Eric Jun 20 '19 at 11:31
  • @Eric I've updated my answer with passing vars to the namespace. – MartenCatcher Jun 20 '19 at 11:38
  • funny, we found the same solution at the same time. Thanks. But it does not explain why when I specify the locals parameter in exec() it causes ONLY variables in list comprehension TEST to be undefined... – Eric Jun 20 '19 at 11:44
0

I finally found a solution, but I find this really ugly :

myglob_var = 'my global var'

def myfunc():
    flag = True
    users = ['root','service']
    s = """
print('myglob_var =',myglob_var)
c_list = [ u for u in users ]
print('list comprehension =',c_list)
c_list_with_test = [ u for u in users if flag ]
print('list comprehension with test =',c_list_with_test)
some_result = 'a result'
"""
    nm = globals().copy()
    nm.update(locals())
    exec(s, nm)
    print('Result =',nm.get('some_result'))


myfunc()

It gives:

$ python3 test.py
myglob_var = my global var
list comprehension = ['root', 'service']
list comprehension with test = ['root', 'service']
Result = a result

it does not explain why when I specify the locals parameter in exec() it causes ONLY variables in list comprehension TEST to be undefined...

Eric
  • 4,821
  • 6
  • 33
  • 60