¿Cómo cargar módulos comstackdos de python desde la memoria?

Necesito leer todos los módulos (precomstackdos) de un archivo zip (creado por py2exe comprimido) en la memoria y luego cargarlos todos. Sé que esto se puede hacer cargando directamente desde el archivo zip, pero necesito cargarlos desde la memoria. ¿Algunas ideas? (Estoy usando python 2.5.2 en windows) TIA Steve

Depende de lo que tenga exactamente como “el módulo (precomstackdo)”. Supongamos que es exactamente el contenido de un archivo ciao.pyc , por ejemplo, ciao.pyc según lo creado por:

 $ cat>'ciao.py' def ciao(): return 'Ciao!' $ python -c'import ciao; print ciao.ciao()' Ciao! 

IOW, habiendo construido así ciao.pyc , di que ahora haces:

 $ python Python 2.5.1 (r251:54863, Feb 6 2009, 19:02:12) [GCC 4.0.1 (Apple Inc. build 5465)] on darwin Type "help", "copyright", "credits" or "license" for more information. >>> b = open('ciao.pyc', 'rb').read() >>> len(b) 200 

y su objective es pasar de esa cadena de bytes b a un módulo importable ciao . Así es cómo:

 >>> import marshal >>> c = marshal.loads(b[8:]) >>> c  at 0x65188, file "ciao.py", line 1> 

Así es como se obtiene el objeto de código del contenido binario .pyc . Edición : si tiene curiosidad, los primeros 8 bytes son un "número mágico" y una marca de tiempo, no se necesitan aquí (a menos que desee verificarlos con cordura y generar excepciones si es necesario, pero eso parece estar fuera del scope de la pregunta) ; marshal.loads boostá de todos modos si detecta una cadena dañada).

Entonces:

 >>> import types >>> m = types.ModuleType('ciao') >>> import sys >>> sys.modules['ciao'] = m >>> exec c in m.__dict__ 

es decir: haga un nuevo objeto de módulo, instálelo en sys.modules , sys.modules ejecutando el objeto de código en su __dict__ . Edición : el orden en el que sys.modules inserción sys.modules y exec importa si y solo si tiene importaciones circulares, pero este es el orden que normalmente utiliza la import propia de Python, por lo que es mejor imitarlo (que no tiene ninguna especificación específica). desventajas).

Puede "crear un nuevo objeto de módulo" de varias maneras (por ejemplo, desde funciones en módulos de biblioteca estándar como new e imp ), pero "llamar al tipo para obtener una instancia" es la forma normal de Python en estos días, y el lugar normal para obtener el tipo de (a menos que tenga un nombre incorporado o de lo contrario ya lo tengas a mano) es de los types módulos de biblioteca estándar, así que eso es lo que recomiendo.

Ahora, finalmente:

 >>> import ciao >>> ciao.ciao() 'Ciao!' >>> 

... puede importar el módulo y usar sus funciones, clases, etc. Otras declaraciones de import (y from ) encontrarán el módulo como sys.modules['ciao'] , por lo que no tendrá que repetir esta secuencia de operaciones (de hecho, no necesita esta última statement de import aquí si lo desea). es para asegurar que el módulo esté disponible para importar desde otro lugar; lo estoy agregando solo para mostrar que funciona ;-).

Edición : si es absolutamente necesario importar de esta manera paquetes y módulos de los mismos, en lugar de "módulos simples" como acabo de mostrar, también es factible, pero un poco más complicado. Como esta respuesta ya es bastante larga, y espero que pueda simplificar su vida si se apega a los módulos simples para este propósito, voy a eludir esa parte de la respuesta ;-).

También tenga en cuenta que esto puede o no hacer lo que desea en casos de "cargar el mismo módulo desde la memoria varias veces" (esto reconstruye el módulo cada vez; es posible que desee verificar sys.modules y simplemente omitir todo si el módulo ya está allí. ) y en particular cuando dicha "carga desde la memoria" repetida se produce desde varios subprocesos (que requieren lockings, pero una architecture mejor es tener un único subproceso dedicado dedicado a realizar la tarea, con otros módulos que se comunican con él a través de una Cola).

Finalmente, no hay discusión sobre cómo instalar esta funcionalidad como un "gancho de importación" transparente que se involucra automáticamente en los mecanismos de las declaraciones internas de import sí mismas. Eso también es factible, pero no es exactamente lo que está preguntando, así que aquí También espero que pueda simplificar su vida haciendo las cosas de la manera más simple, como lo indica esta respuesta.

El archivo comstackdo de Python consiste en

  1. número mágico (4 bytes) para determinar el tipo y la versión de Python,
  2. marca de tiempo (4 bytes) para verificar si tenemos una fuente más nueva,
  3. objeto de código calculado.

Para cargar el módulo, tiene que crear un objeto de módulo con imp.new_module() , ejecutar código sin empalmar en el espacio de nombres del nuevo módulo y ponerlo en sys.modules . A continuación en la implementación de la muestra:

 import sys, imp, marshal def load_compiled_from_memory(name, filename, data, ispackage=False): if data[:4]!=imp.get_magic(): raise ImportError('Bad magic number in %s' % filename) # Ignore timestamp in data[4:8] code = marshal.loads(data[8:]) imp.acquire_lock() # Required in threaded applications try: mod = imp.new_module(name) sys.modules[name] = mod # To handle circular and submodule imports # it should come before exec. try: mod.__file__ = filename # Is not so important. # For package you have to set mod.__path__ here. # Here I handle simple cases only. if ispackage: mod.__path__ = [name.replace('.', '/')] exec code in mod.__dict__ except: del sys.modules[name] raise finally: imp.release_lock() return mod 

Actualización : el código se actualiza para manejar paquetes correctamente.

Tenga en cuenta que debe instalar el enlace de importación para manejar las importaciones dentro de los módulos cargados. Una forma de hacerlo es agregar su buscador en sys.meta_path . Ver PEP302 para más información.