0

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.

10
  • exec() takes globals and locals dictionaries. Just pass in empty ones, or pass in a new module object dictionary. That's what the accepted answer does. Commented May 14, 2016 at 18:30
  • This is code I copied directly from the link I provided. Part of my problem is I don't understand this code completely. Which is why I can't make it work for my scenario. Commented May 14, 2016 at 18:30
  • 1
    Just use the accepted answer there instead, where they use a blank module object and pass in the globals dictionary to exec(). Commented May 14, 2016 at 18:34
  • 1
    Ah, I've written an answer now, which will makes it impossible for you to delete this yourself now. Provided the other answer doesn't get a vote, if I delete my answer you can self-delete. If you really want to, that's what I can do. Commented May 14, 2016 at 18:48
  • 1
    OTOH, having an answer to your question means anyone else finding that code and wondering what to do now will know they should avoid that. Commented May 14, 2016 at 18:48

2 Answers 2

2

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.

Sign up to request clarification or add additional context in comments.

Comments

0

In general, this is not how you should dynamically import and use external modules. You should be using __import__ within your function to do this. Here's a simple example that worked for me:

plt = __import__('matplotlib.pyplot', fromlist = ['plt'])
plt.plot(np.arange(5), np.arange(5))
plt.show()

I imagine that for your specific application (loading from code string) it would be much easier to save the dynamically generated code string to a file (in a folder containing an __init__.py file) and then to call it using __import__. Then you could access all variables and functions of the code as parts of the imported module.

Unless I'm missing something?

4 Comments

They are creating a module from a string containing code, not a file on disk, which is what you are importing.
I know, that's why I suggested saving the string to a file and then reload it with __import__. May not be the ideal solution he's looking for, but I think that this way would be a lot more understandable for anyone else who happens to read the code.
The linked post has much better answers though. exec() works fine for that case, provided you give it a better globals target, which is what the accepted answer does.
Sorry, I didn't see your last post before making my comment. I should've taken a look before writing.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.