2009-12-02 9 views
6

Necesito leer todos los módulos (precompilados) de un archivo comprimido (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. Alguna idea? (Estoy usando python 2.5.2 en Windows) TIA SteveCómo cargar módulos compilados de python de la memoria?

+0

¿Puedes darnos más información? ¿Por qué necesita cargar el código en la memoria en primer lugar? ¿Por qué no se ajusta la carga desde un archivo ZIP? – Ber

Respuesta

30

Depende de lo que tenga exactamente como "el módulo (precompilado)". Vamos a suponer que es exactamente el contenido de un archivo .pyc, por ejemplo, ciao.pyc como construido por:

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

OIA, habiendo así construida ciao.pyc, dice que ahora lo hace:

$ 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 objetivo es vaya desde esa cadena de bytes b a un módulo importable ciao. Así es como:

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

así es como se consigue el objeto de código de los .pyc contenido binario. Editar: si tiene curiosidad, los primeros 8 bytes son un "número mágico" y una marca de tiempo: no es necesario aquí (a menos que desee verificar su cordura y plantear excepciones si está justificado, pero eso parece estar fuera del alcance de la pregunta; marshal.loads aumentará de todos modos si detecta una cadena dañada).

continuación:

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

es decir: hacer un nuevo objeto de módulo, instalarlo en sys.modules, poblarlo mediante la ejecución del objeto de código en su __dict__. Editar: el orden en el que realiza la inserción sys.modules y exec importa si y solo si puede tener importaciones circulares, pero este es el orden que utiliza normalmente el propio import de Python, por lo que es mejor imitarlo (lo que no tiene 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 y 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 tenga) es del módulo de biblioteca estándar types, así que eso es lo que recomiendo.

Ahora, por fin:

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

... puede importar el módulo y el uso de sus funciones, clases, y así sucesivamente. Otras declaraciones 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 declaración import aquí si todo lo que desea es garantizar el módulo está disponible para importar desde otro lugar; lo estoy agregando solo para mostrar que funciona ;-).

Editar: Si tiene que importar paquetes y módulos de esta manera, en lugar de "módulos simples" como acabo de mostrar, eso también es factible, pero un poco más complicado. Como esta respuesta ya es bastante larga, y espero que pueda simplificar su vida apegándose a 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 puede hacer lo que quiera en los casos de "cargar el mismo módulo de memoria varias veces" (esto reconstruye el módulo cada vez; es posible que desee comprobar sys.modules y sólo pasa todo si el módulo ya está allí) y en particular cuando esa repetida "carga desde la memoria" ocurre desde múltiples hilos (que necesitan bloqueos, pero una mejor arquitectura es tener un solo hilo dedicado para realizar la tarea, con otros módulos comunicándose con ella a través de una fila).

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

+1

Simplemente curioso, ¿Funcionará también para archivos .pyd? – YOU

+1

Estaba teniendo el mismo requisito y estaba buscando una solución, gracias Alex :) – Parthan

+0

@ S.Mark, no, '.pyc' o' .pyo' solamente - '.pyd's son esencialmente' .dll's bajo un nombre falso y presente ** completamente ** problemas diferentes. @Technofreak, de nada! -) –

9

Compilado archivo Python consisten 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 comprobar si tenemos nueva fuente,
  3. código calculan las referencias objeto.

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

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 tu buscador al sys.meta_path. Consulte PEP302 para obtener más información.

Cuestiones relacionadas