alecrasmussen alecrasmussen - 5 months ago 20
Python Question

Recursion at exec() level with globals

After stumbling upon this question I've been playing around with exec() to better understand how it works. I'm trying to get this abomination of a script to increment a global variable, read itself, and call exec with the new global value until the recursion limit is met. The best I've been able to come up with is having one file declare the global and then call the next file (an exact duplicate minus the variable declaration) which will then recursively call itself. Here's the code for the first one:

# recurse.py

def func():
global x
x += 1
with open('recurse2.py', 'r') as f:
try:
exec(f.read(), {'x': x})
except RecursionError:
print('maximum recursion depth reached at', x)

x = 0
func()


And here's the file it executes, that will execute itself:

# recurse2.py

def func():
global x
x += 1
with open('recurse2.py', 'r') as f:
try:
exec(f.read(), {'x': x})
except RecursionError:
print('maximum recursion depth reached at', x)

func()


Is it possible to achieve the same effect with only one file?

Answer

You could do this:

# recurse.py

def func():
    global x
    x += 1
    with open('recurse.py', 'r') as f:    
        try:
            exec(f.read(), {'x': x})
        except RuntimeError:
            print('maximum recursion depth reached at', x)

try:
    x
except NameError:
    x = 0
func()

The problem with your initial example isn't really specific to exec. It's just that the program itself sets x to zero before calling func. So passing in a starting value of x has no effect: the code you are exec-ing sets a new value of x anyway. In this new version, a try/except block tests whether the name already exists before initializing it to zero.

(I used RuntimeError here because I don't have Python 3.5, where RecursionError was introduced, but it should work the same with RecursionError.)