¿Cómo cargar un módulo desde el código en una cadena?

Tengo algo de código en forma de cadena y me gustaría hacer un módulo fuera de él sin escribir en el disco.

Cuando bash usar imp y un objeto StringIO para hacer esto, obtengo:

>>> imp.load_source('my_module', '', StringIO('print "hello world"')) Traceback (most recent call last): File "", line 1, in  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 "", line 1, in  ValueError: load_module arg#2 should be a file or None 

¿Cómo puedo crear el módulo sin tener un archivo real? Alternativamente, ¿cómo puedo envolver un StringIO en un archivo sin escribir en el disco?

ACTUALIZAR:

NOTA: Este problema también es un problema en python3.

El código que estoy intentando cargar solo es de confianza parcial. Lo revisé con ast y determiné que no importa nada ni hace nada que no me guste, pero no confío lo suficiente como para ejecutarlo cuando tengo variables locales que se pueden modificar, y No confío en que mi propio código se mantenga fuera del camino que estoy intentando importar.

Creé un módulo vacío que solo contiene lo siguiente:

 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] 

Luego puede importar mymodule y llamar a mymodule.load(code) . Esto me funciona porque me he asegurado de que el código que estoy cargando no use globals . Además, la palabra clave global es solo una directiva de analizador y no puede referirse a nada fuera del ejecutivo.

Esto realmente es demasiado trabajo para import el módulo sin escribir en el disco, pero si alguna vez quieres hacer esto, creo que es la mejor manera.

Aquí es cómo importar una cadena como un módulo ( Python 2.x ):

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

En Python 3 , exec es una función, por lo que debería funcionar:

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

Ahora acceda a los atributos del módulo (y funciones, clases, etc.) como:

 print(mymodule.a) >>> 5 

Para ignorar cualquier próximo bash de importación, agregue el módulo a sys :

 sys.modules['mymodule'] = mymodule 

Si el código del módulo está en una cadena, puede renunciar al uso de StringIO y usarlo directamente con exec , como se ilustra a continuación con un archivo llamado dynmodule.py . Funciona en Python 2 y 3.

 from __future__ import print_function class _DynamicModule(object): def load(self, code): execdict = {'__builtins__': None} # optional, to increase safety exec(code, execdict) keys = execdict.get( '__all__', # use __all__ attribute if defined # else all non-private attributes (key for key in execdict if not key.startswith('_'))) for key in keys: setattr(self, key, execdict[key]) # replace this module object in sys.modules with empty _DynamicModule instance # see Stack Overflow question: # https://stackoverflow.com/questions/5365562/why-is-the-value-of-name-changing-after-assignment-to-sys-modules-name import sys as _sys _ref, _sys.modules[__name__] = _sys.modules[__name__], _DynamicModule() if __name__ == '__main__': import dynmodule # name of this module import textwrap # for more readable code formatting in sample string # string to be loaded can come from anywhere or be generated on-the-fly module_code = textwrap.dedent("""\ foo, bar, baz = 5, 8, 2 def func(): return foo*bar + baz __all__ = 'foo', 'bar', 'func' # 'baz' not included """) dynmodule.load(module_code) # defines module's contents print('dynmodule.foo:', dynmodule.foo) try: print('dynmodule.baz:', dynmodule.baz) except AttributeError: print('no dynmodule.baz attribute was defined') else: print('Error: there should be no dynmodule.baz module attribute') print('dynmodule.func() returned:', dynmodule.func()) 

Salida:

 dynmodule.foo: 5 no dynmodule.baz attribute was defined dynmodule.func() returned: 42 

Establecer la entrada '__builtins__' en None en el diccionario de execdict impide que el código ejecute directamente cualquier función incorporada, como __import__ , y así hace que la ejecución sea más segura. Puede aliviar esa restricción agregando cosas a ella de manera selectiva que considere que son aceptables y / o necesarias.

También es posible agregar sus propias utilidades y atributos predefinidos que le gustaría que estuvieran disponibles para el código, creando así un contexto de ejecución personalizado para que se ejecute. Ese tipo de cosas pueden ser útiles para implementar un “complemento” u otro Arquitectura extensible por el usuario.

Simplemente puede crear un objeto de Módulo y meterlo en sys.modules y poner su código dentro.

Algo como:

 import sys from types import ModuleType mod = ModuleType('mymodule') sys.modules['mymodule'] = mod exec(mycode, mod.__dict__) 

podría usar exec o eval para ejecutar el código de Python como una cadena. ver aquí , aquí y aquí

La documentación para imp.load_source dice (mi énfasis):

El argumento del archivo es el archivo fuente, abierto para leer como texto, desde el principio. Actualmente debe ser un objeto de archivo real, no una clase definida por el usuario que emule un archivo.

… Me temo que no tendrás suerte con este método.

Tal vez eval sería suficiente para usted en este caso?

Sin embargo, esto parece un requisito bastante sorprendente: puede ser útil si agrega algo más a su pregunta sobre el problema que realmente está tratando de resolver.

imp.new_module está en desuso desde Python 3.4

pero la solución corta de schlenk utilizando tipos.ModuleType aún funciona en Python 3.7

imp.new_module se reemplazó con importlib.util.module_from_spec

Se prefiere importlib.util.module_from_spec sobre el uso de types.ModuleType para crear un nuevo módulo, ya que las especificaciones se usan para establecer tantos atributos controlados por importación en el módulo como sea posible.

importlib.util.spec_from_loader utiliza las API disponibles del cargador, como InspectLoader.is_package (), para completar la información faltante en la especificación.

pero tanto la versión corta como la larga solo establecerán: __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()