2009-12-02 4 views
6

J'ai besoin de lire tous les modules (pré-compilés) à partir d'un fichier zip (construit par py2exe compressé) en mémoire, puis les charger tous. Je sais que cela peut être fait en chargeant directement à partir du fichier zip mais j'ai besoin de les charger de la mémoire. Des idées? (J'utilise python 2.5.2 sur windows) TIA SteveComment charger les modules python compilés de la mémoire?

+0

Pouvez-vous donner plus de fond? Pourquoi avez-vous besoin de charger le code dans la mémoire en premier lieu? Pourquoi le chargement à partir d'un fichier ZIP ne correspond pas? – Ber

Répondre

30

Cela dépend de ce que vous avez exactement comme "le module (pré-compilé)". Supposons que c'est exactement le contenu d'un fichier .pyc, par exemple, ciao.pyc comme construit par:

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

OIEau, ayant ainsi construit ciao.pyc, dites que vous faites:

$ 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 

et votre objectif est de passer de cette chaîne d'octets b à un module importable ciao. Voici comment:

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

est ce que vous obtenez l'objet de code à partir du .pyc contenu binaire. Modifier: si vous êtes curieux, les 8 premiers octets sont un "nombre magique" et un horodatage - pas nécessaire ici (sauf si vous voulez les vérifier et lever des exceptions si nécessaire, mais cela semble hors de la portée de la question marshal.loads soulèvera quand même s'il détecte une chaîne corrompue).

Puis:

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

i.e.: faire un nouvel objet module, installez-le dans sys.modules, le remplir en exécutant l'objet de code dans son __dict__. Modifier: l'ordre dans lequel vous faites l'insertion sys.modules et exec questions si et seulement si vous pouvez avoir des importations circulaires - mais, c'est l'ordre import de Python utilise normalement, donc il est préférable de l'imiter (qui n'a pas de inconvénients). Vous pouvez "créer un nouvel objet module" de plusieurs façons (par exemple, à partir de fonctions dans des modules de bibliothèque standard tels que new et imp), mais "appeler le type pour obtenir une instance" est la méthode Python normale de nos jours, et l'endroit normal pour obtenir le type de (à moins qu'il ait un nom intégré ou que vous l'ayez déjà à portée de la main) provient du module de bibliothèque standard types, donc c'est ce que je recommande.

Maintenant, enfin:

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

... vous pouvez importer le module et utiliser ses fonctions, classes, et ainsi de suite. Autres import (et from) déclarations seront ensuite trouver le module comme sys.modules['ciao'], de sorte que vous ne devrez répéter cette séquence d'opérations (en effet, vous n'avez pas besoin cette dernière déclaration import ici si vous voulez tout est d'assurer que le module est disponible pour l'importation d'ailleurs - je l'ajoute seulement pour montrer que cela fonctionne ;-).

Modifier: Si vous devez absolument importer de cette façon des paquets et modules, plutôt que des "modules simples" comme je viens de le montrer, c'est faisable, aussi, mais un peu plus compliqué. Comme cette réponse est déjà assez longue, et j'espère que vous pourrez vous simplifier la vie en vous contentant de modules simples à cet effet, je vais me soustraire à cette partie de la réponse ;-).Notez également que cela peut ou ne peut pas faire ce que vous voulez dans le cas de "charger plusieurs fois le même module de la mémoire" (cela reconstruit le module à chaque fois, vous pouvez vouloir vérifier sys.modules et juste sauter tout si le module est déjà là) et en particulier quand une telle "charge de mémoire" répétée provient de plusieurs threads (nécessitant des verrous - mais une meilleure architecture est d'avoir un seul thread dédié dédié à l'exécution de la tâche, avec d'autres modules communiquant avec une queue). Enfin, il n'y a aucune discussion sur la façon d'installer cette fonctionnalité en tant que «crochet d'importation» transparent qui s'implique automagiquement dans les mécanismes des internes de la déclaration import - c'est faisable, mais pas exactement ce que vous demandez à propos de, alors ici aussi, j'espère que vous pouvez vous simplifier la vie en faisant les choses de la façon la plus simple, comme le souligne cette réponse.

+1

Juste curieux, Est-ce que ça marchera aussi pour les fichiers .pyd? – YOU

+1

J'avais la même exigence et je cherchais une solution, merci Alex :) – Parthan

+0

@ S.Mark, non, '.pyc' ou' .pyo' seulement '' .pyd's sont essentiellement '.dll's sous un faux nom et présente ** complètement ** différents problèmes. @ Technofreak, vous êtes les bienvenus! -) –

9

Compilé fichier Python se composent de

  1. nombre magique (4 octets) pour déterminer le type et la version de Python,
  2. horodatage
  3. (4 octets) pour vérifier si nous avons plus récente source
  4. Code marshalés objet.

Pour charger le module, vous devez créer un objet de module avec imp.new_module(), exécuter du code unmashaled dans l'espace de noms de module et de le mettre en sys.modules. Ci-dessous la mise en œuvre de l'échantillon:

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 

Mise à jour: le code est mis à jour pour gérer correctement les paquets.

Notez que vous devez installer le point d'ancrage d'importation pour gérer les importations dans les modules chargés. Une façon de faire est d'ajouter votre finder dans sys.meta_path. Voir PEP302 pour plus d'informations.

Questions connexes