Beaker Beaker - 6 months ago 9
Python Question

executing python code from string loaded into a module

I found the following code snippet that I can't seem to make work for my scenario (or any scenario at all):

def load(code):
# Delete all local variables
globals()['code'] = code
del locals()['code']

# Run the code
exec(globals()['code'])

# Delete any global variables we've added
del globals()['load']
del globals()['code']

# Copy k so we can use it
if 'k' in locals():
globals()['k'] = locals()['k']
del locals()['k']

# Copy the rest of the variables
for k in locals().keys():
globals()[k] = locals()[k]


I created a file called "dynamic_module" and put this code in it, which I then used to try to execute the following code which is a placeholder for some dynamically created string I would like to execute.

import random
import datetime


class MyClass(object):
def main(self, a, b):
r = random.Random(datetime.datetime.now().microsecond)
a = r.randint(a, b)
return a


Then I tried executing the following:

import dynamic_module
dynamic_module.load(code_string)
return_value = dynamic_module.MyClass().main(1,100)


When this runs it should return a random number between 1 and 100. However, I can't seem to get the initial snippet I found to work for even the simplest of code strings. I think part of my confusion in doing this is that I may misunderstand how globals and locals work and therefore how to properly fix the problems I'm encountering. I need the code string to use its own imports and variables and not have access to the ones where it is being run from, which is the reason I am going through this somewhat over-complicated method.

Answer

You should not be using the code you found. It is has several big problems, not least that most of it doesn't actually do anything (locals() is a proxy, deleting from it has no effect on the actual locals, it puts any code you execute in the same shared globals, etc.)

Use the accepted answer in that post instead; recast as a function that becomes:

import sys, imp

def load_module_from_string(code, name='dynamic_module')
    module = imp.new_module(name)
    exec(code, mymodule.__dict__)
    return module

then just use that:

dynamic_module = load_module_from_string(code_string)
return_value = dynamic_module.MyClass().main(1, 100)

The function produces a new, clean module object.