0

Question:

It feels like the following Python code should be able to run in all circumstances (if print and range is kept unchanged obviously).

for i in range(5):
    print(i)
    print([(i,j) for j in range(i)])

When run under exec, I find some situations where this code returns an error. Is this an error on Python's side, or am I missing something?

Minimum working example:

import traceback

code_str = r"""
print('\nExec output:')
for i in range(3):
    print(i)
    print([(i,j) for j in range(i)])
"""

exec_params = [[None, None],
               [None, globals()],
               [locals(), None],
               [{}, None],
               [{}, {}],
               [None, {}]]

for param0, param1 in exec_params:
    try:
        #scrubb all variables that could leak from exec
        try: del i
        except: pass

        exec(code_str, param0, param1)

    except Exception as e:
        print(traceback.format_exc())

Output:

   Python 2.7.12    |    Python 3.4.4                                                
                    |                                                                
-------------------------------------------------------------------------------------

Params: None, None

-------------------------------------------------------------------------------------
Exec output:        | Exec output:                                                   
0                   | 0                                                              
[]                  | []                                                             
1                   | 1                                                              
[(1, 0)]            | [(1, 0)]                                                       
2                   | 2                                                              
[(2, 0), (2, 1)]    | [(2, 0), (2, 1)]                                               
-------------------------------------------------------------------------------------

Params: None, globals()

-------------------------------------------------------------------------------------
Exec output:        | Exec output:                                                   
0                   | 0                                                              
[]                  | []                                                             
1                   | 1                                                              
[(1, 0)]            | [(1, 0)]                                                       
2                   | 2                                                              
[(2, 0), (2, 1)]    | [(2, 0), (2, 1)]                                               
-------------------------------------------------------------------------------------

Params: locals(), None

-------------------------------------------------------------------------------------
Exec output:        | Exec output:                                                   
0                   | 0                                                              
[]                  | []                                                             
1                   | 1                                                              
[(1, 0)]            | [(1, 0)]                                                       
2                   | 2                                                              
[(2, 0), (2, 1)]    | [(2, 0), (2, 1)]                                               
-------------------------------------------------------------------------------------

Params: {}, None

-------------------------------------------------------------------------------------
Exec output:        | Exec output:                                                   
0                   | 0                                                              
[]                  | []                                                             
1                   | 1                                                              
[(1, 0)]            | [(1, 0)]                                                       
2                   | 2                                                              
[(2, 0), (2, 1)]    | [(2, 0), (2, 1)]                                               
-------------------------------------------------------------------------------------

Params: {}, {}

-------------------------------------------------------------------------------------
Exec output:        | Exec output:                                                   
0                   | 0                                                              
[]                  | []                                                             
1                   | 1                                                              
[(1, 0)]            | Traceback (most recent call last):                             
2                   |   File "<ipython-input-126-fe6125860589>", line 23, in <module>
[(2, 0), (2, 1)]    |     exec(code_str, param0, param1)                             
                    |   File "<string>", line 5, in <module>                         
                    |   File "<string>", line 5, in <listcomp>                       
                    | NameError: name 'i' is not defined                             
-------------------------------------------------------------------------------------

Params: None, {}

-------------------------------------------------------------------------------------
Exec output:        | Exec output:                                                   
0                   | 0                                                              
[]                  | []                                                             
1                   | 1                                                              
[(1, 0)]            | Traceback (most recent call last):                             
2                   |   File "<ipython-input-126-fe6125860589>", line 23, in <module>
[(2, 0), (2, 1)]    |     exec(code_str, param0, param1)                             
                    |   File "<string>", line 5, in <module>                         
                    |   File "<string>", line 5, in <listcomp>                       
                    | NameError: name 'i' is not defined                             
Simon Streicher
  • 2,638
  • 1
  • 26
  • 30
  • In Python 3, list comprehensions got their own scope. – Martijn Pieters Apr 14 '17 at 11:26
  • Note that your code would either run with `i` as a global (where `locals() is globals()` is true), *or* as a local, making `i` a *closure* to the list comprehension scope. By supplying two separate dictionaries to `exec()` you declared `i` to be a local, but without a function scope the closure isn't created. – Martijn Pieters Apr 14 '17 at 11:29
  • The work-around is the same as for the duplicate: create a new scope to pass to exec, putting the list comprehension and the variables it needs access to in to a function scope. – Martijn Pieters Apr 14 '17 at 11:29
  • Okay, so it boils down to the list comprehension's scope. But shouldn't that scope still see the scope that it was spawn from? – Simon Streicher Apr 14 '17 at 11:36
  • `exec()` can't create a closure when you don't have a function scope. That's what is missing here. – Martijn Pieters Apr 14 '17 at 11:37
  • This is all documented in the [`exec()` function documentation](https://docs.python.org/3/library/functions.html#exec), including *the code that’s executed is expected to be valid as file input (see the section “File input” in the Reference Manual).* and *Remember that at module level, globals and locals are the same dictionary. If `exec` gets two separate objects as globals and locals, the code will be executed as if it were embedded in a class definition.* – Martijn Pieters Apr 14 '17 at 11:41
  • (and to clarify, [*File Input*](https://docs.python.org/3/reference/toplevel_components.html#file-input) is basically a stand-alone script or module. The context in which `exec()` was executed doesn't play a role (nor can it, because `exec()` is just a function). – Martijn Pieters Apr 14 '17 at 11:47

0 Answers0