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.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.