Beaker Beaker - 1 year ago 47
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 Source

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.