Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to load a module from code in a string?

Tags:

python

I have some code in the form of a string and would like to make a module out of it without writing to disk.

When I try using imp and a StringIO object to do this, I get:

>>> imp.load_source('my_module', '', StringIO('print "hello world"')) Traceback (most recent call last):   File "<stdin>", line 1, in <module> TypeError: load_source() argument 3 must be file, not instance >>> imp.load_module('my_module', StringIO('print "hello world"'), '', ('', '', 0)) Traceback (most recent call last):   File "<stdin>", line 1, in <module> ValueError: load_module arg#2 should be a file or None 

How can I create the module without having an actual file? Alternatively, how can I wrap a StringIO in a file without writing to disk?

UPDATE:

NOTE: This issue is also a problem in python3.

The code I'm trying to load is only partially trusted. I've gone through it with ast and determined that it doesn't import anything or do anything I don't like, but I don't trust it enough to run it when I have local variables running around that could get modified, and I don't trust my own code to stay out of the way of the code I'm trying to import.

I created an empty module that only contains the following:

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] 

Then you can import mymodule and call mymodule.load(code). This works for me because I've ensured that the code I'm loading does not use globals. Also, the global keyword is only a parser directive and can't refer to anything outside of the exec.

This really is way too much work to import the module without writing to disk, but if you ever want to do this, I believe it's the best way.

like image 475
Conley Owens Avatar asked Mar 19 '11 14:03

Conley Owens


People also ask

How do you load a module in Python?

To load the default version of Python module, use module load python . To select a particular software version, use module load python/version . For example, use module load python/3.5 to load the latest version of Python 3.5. After the module is loaded, you can run the interpreter by using the command python .

How do I import a module into a module?

You need to use the import keyword along with the desired module name. When interpreter comes across an import statement, it imports the module to your current program. You can use the functions inside a module by using a dot(.) operator along with the module name.

Can you import a module in a function?

Importing Modules To make use of the functions in a module, you'll need to import the module with an import statement. An import statement is made up of the import keyword along with the name of the module. In a Python file, this will be declared at the top of the code, under any shebang lines or general comments.


2 Answers

Here is how to import a string as a module (Python 2.x):

import sys,imp  my_code = 'a = 5' mymodule = imp.new_module('mymodule') exec my_code in mymodule.__dict__ 

In Python 3, exec is a function, so this should work:

import sys,imp  my_code = 'a = 5' mymodule = imp.new_module('mymodule') exec(my_code, mymodule.__dict__) 

Now access the module attributes (and functions, classes etc) as:

print(mymodule.a) >>> 5 

To ignore any next attempt to import, add the module to sys:

sys.modules['mymodule'] = mymodule 
like image 170
Remi Avatar answered Oct 12 '22 02:10

Remi


imp.new_module is deprecated since python 3.4

but the short solution from schlenk using types.ModuleType is still working in python 3.7

imp.new_module was replaced with importlib.util.module_from_spec

importlib.util.module_from_spec is preferred over using types.ModuleType to create a new module as spec is used to set as many import-controlled attributes on the module as possible.

importlib.util.spec_from_loader uses available loader APIs, such as InspectLoader.is_package(), to fill in any missing information on the spec.

these module attributes are __builtins__, __doc__, __loader__, __name__, __package__, __spec__

import sys, importlib  my_name = 'my_module' my_spec = importlib.util.spec_from_loader(my_name, loader=None)  my_module = importlib.util.module_from_spec(my_spec)  my_code = ''' def f():     print('f says hello') ''' exec(my_code, my_module.__dict__) sys.modules['my_module'] = my_module  my_module.f() 
like image 35
Mila Nautikus Avatar answered Oct 12 '22 04:10

Mila Nautikus