2009-12-01 4 views
10

Afin de réduire le temps de développement de mon application web basée sur Python, j'essaie d'utiliser reload() pour les modules que j'ai récemment modifiés. Le reload() passe par une page web dédiée (partie de la version de développement de l'application web) qui liste les modules qui ont été récemment modifiés (et l'horodatage modifié du fichier py est postérieur au fichier pyc correspondant). La liste complète des modules est obtenue à partir de sys.modules (et je filtre la liste pour me concentrer uniquement sur les modules qui font partie de mon paquet).comment trouver la liste des modules qui dépendent d'un module spécifique en python

Le rechargement de fichiers python individuels semble fonctionner dans certains cas et pas dans d'autres. Je suppose que tous les modules qui dépendent d'un module modifié devraient être rechargés et que le rechargement devrait se faire dans le bon ordre.

Je cherche un moyen d'obtenir la liste des modules importés par un module spécifique. Y a-t-il un moyen de faire ce genre d'introspection en Python? Je comprends que mon approche pourrait ne pas être garantie à 100% et le moyen le plus sûr serait de tout recharger, mais si une approche rapide fonctionne dans la plupart des cas, elle serait suffisante pour le développement.

Réponse aux commentaires concernant django autoreloader

@Glenn Maynard, Thanx, j'avais lu le autoreloader de django. Mon application Web est basée sur Zope 3 et avec le nombre de paquets et beaucoup d'initialisations basées sur ZCML, le redémarrage total prend environ 10 secondes à 30 secondes ou plus si la taille de la base de données est plus grande. J'essaye de réduire ce temps passé pendant le redémarrage. Quand je sens que j'ai fait beaucoup de changements, je préfère habituellement faire un redémarrage complet, mais le plus souvent je change quelques lignes ici et là pour lesquelles je ne veux pas passer autant de temps. La configuration du développement est complètement indépendante de la configuration de la production et généralement, si quelque chose ne va pas dans le rechargement, cela devient évident puisque les pages de l'application commencent à montrer des informations illogiques ou à lancer des exceptions. Je suis très intéressé à explorer si le rechargement sélectif fonctionnerait ou non.

+0

Il est beaucoup plus sûr de faire quelque chose comme le autoreloader de Django, qui ré-exécute entièrement le backend quand un fichier source est modifié. Je ne connais aucun désavantage; vous modifiez un fichier et tout est rechargé une seconde ou deux plus tard, automatiquement. Quelque chose qui ne fonctionne que dans la «plupart des cas» est très mauvais pour le développement; vous demandez simplement d'être mordu douloureusement sur la route quand ce n'est pas le cas. –

+0

Revisiter ici en raison d'une question en double, et en ajoutant que "la façon de minimiser le temps de rechargement lors de l'utilisation de zope" est d'utiliser sauna.reload maintenant (2013) – jsbueno

Répondre

5

Alors - cela répond « Trouver une liste de modules qui dépendent d'une donnée un "- au lieu de comment la question a été formulée à l'origine - que j'ai répondu ci-dessus. Il s'avère que c'est un peu plus complexe: il faut trouver l'arbre de dépendance pour tous les modules chargés, et l'inverser pour chaque module, tout en conservant un ordre de chargement qui ne casserait pas les choses.

J'avais aussi publié ce billet au wiki python brazillian à: http://www.python.org.br/wiki/RecarregarModulos

#! /usr/bin/env python 
# coding: utf-8 

# Author: João S. O. Bueno 
# Copyright (c) 2009 - Fundação CPqD 
# License: LGPL V3.0 


from types import ModuleType, FunctionType, ClassType 
import sys 

def find_dependent_modules(): 
    """gets a one level inversed module dependence tree""" 
    tree = {} 
    for module in sys.modules.values(): 
     if module is None: 
      continue 
     tree[module] = set() 
     for attr_name in dir(module): 
      attr = getattr(module, attr_name) 
      if isinstance(attr, ModuleType): 
       tree[module].add(attr) 
      elif type(attr) in (FunctionType, ClassType):   
       tree[module].add(attr.__module__) 
    return tree 


def get_reversed_first_level_tree(tree): 
    """Creates a one level deep straight dependence tree""" 
    new_tree = {} 
    for module, dependencies in tree.items(): 
     for dep_module in dependencies: 
      if dep_module is module: 
       continue 
      if not dep_module in new_tree: 
       new_tree[dep_module] = set([module]) 
      else: 
       new_tree[dep_module].add(module) 
    return new_tree 

def find_dependants_recurse(key, rev_tree, previous=None): 
    """Given a one-level dependance tree dictionary, 
     recursively builds a non-repeating list of all dependant 
     modules 
    """ 
    if previous is None: 
     previous = set() 
    if not key in rev_tree: 
     return [] 
    this_level_dependants = set(rev_tree[key]) 
    next_level_dependants = set() 
    for dependant in this_level_dependants: 
     if dependant in previous: 
      continue 
     tmp_previous = previous.copy() 
     tmp_previous.add(dependant) 
     next_level_dependants.update(
      find_dependants_recurse(dependant, rev_tree, 
            previous=tmp_previous, 
            )) 
    # ensures reloading order on the final list 
    # by postponing the reload of modules in this level 
    # that also appear later on the tree 
    dependants = (list(this_level_dependants.difference(
         next_level_dependants)) + 
        list(next_level_dependants)) 
    return dependants 

def get_reversed_tree(): 
    """ 
     Yields a dictionary mapping all loaded modules to 
     lists of the tree of modules that depend on it, in an order 
     that can be used fore reloading 
    """ 
    tree = find_dependent_modules() 
    rev_tree = get_reversed_first_level_tree(tree) 
    compl_tree = {} 
    for module, dependant_modules in rev_tree.items(): 
     compl_tree[module] = find_dependants_recurse(module, rev_tree) 
    return compl_tree 

def reload_dependences(module): 
    """ 
     reloads given module and all modules that 
     depend on it, directly and otherwise. 
    """ 
    tree = get_reversed_tree() 
    reload(module) 
    for dependant in tree[module]: 
     reload(dependant) 

Ce wokred bien dans tous les tests que j'ai fait ici - mais je ne recoment en abuser. Mais pour mettre à jour un serveur zope2 en cours d'exécution après avoir édité quelques lignes de code, je pense que je l'utiliserais moi-même.

+0

Salut, j'ai compris que je cherchais des attributs ModuleType dans le dir (module) n'est pas assez bon.Un nombre de fois les importations ressemblent à '' à partir de xyz import abc'' Pour gérer cela, il faut aussi considérer les attributs FunctionType et ClassType dans la liste dir (module) et pour ceux-là, il faut récupérer leur getattr correspondant (attr, '__module__') et les ajouter dans les dépendances –

+0

En effet, je vais devoir corriger cela - ou enlever le code des deux endroits - il est tellement compelx maintenant qu'il doit "travailler" pour celui qui en a besoin – jsbueno

+0

* FIXED * l'exemple affiché – jsbueno

3

Vous pouvez jeter un oeil à Ian Bicking module Reloader Coller, qui fait ce que vous voulez déjà:

http://pythonpaste.org/modules/reloader?highlight=reloader

Il ne vous donne pas spécifiquement une liste de fichiers dépendants (qui est seulement techniquement possible si le conditionneur a été diligent et correctement spécifié dépendances), mais en regardant le code vous donnera une liste précise des fichiers modifiés pour redémarrer le processus.

2

introspection à la rescousse:

from types import ModuleType 

def find_modules(module, all_mods = None): 
    if all_mods is None: 
     all_mods = set([module]) 
    for item_name in dir(module): 
     item = getattr(module, item_name) 
     if isinstance(item, ModuleType) and not item in all_mods: 
      all_mods.add(item) 
      find_modules(item, all_mods) 
    return all_mods 

Cela vous donne un ensemble avec tous les modules chargés - il suffit d'appeler la fonction avec votre premier module en tant que paramètre unique. Vous pouvez ensuite itérer sur l'ensemble résultant rechargeant, aussi simplement que: [reload (m) m find_modules (<Module>)]

+0

Juste en essayant de comprendre ce code. - commencer par un module donné x - créer un ensemble vide de modules qui ont été importés par x - itéré dir (x) pour identifier tous les éléments qui se trouvent être des modules - les ajouter à l'ensemble des modules pour x - le faire de manière récursive pour trouver toutes les dépendances pour x Je voudrais probablement commencer par cela et obtenir une cartographie inversée pour identifier l'ensemble de tous les modules qui dépendent d'un module spécifique –

+0

Gah, pourquoi voudriez-vous faire cela? sys.modules contient déjà tous les modules chargés .... –

+0

Wait - -Est-ce que vous avez besoin d'une "liste de modules dont dépend un module en python" ou d'une liste "de tous les modules qui dépendent d'un module spécifique"? Je peux trouver un code pour le dernier, pas beaucoup plus complexe que celui-ci - mais la question est formulée pour le premier. – jsbueno

Questions connexes