2009-12-01 9 views
10

Para reducir el tiempo de desarrollo de mi aplicación web basada en Python, estoy intentando usar reload() para los módulos que he modificado recientemente. La recarga() ocurre a través de una página web dedicada (parte de la versión de desarrollo de la aplicación web) que enumera los módulos que se han modificado recientemente (y la marca de tiempo modificada del archivo py es posterior al archivo pyc correspondiente). La lista completa de módulos se obtiene de sys.modules (y filtro la lista para enfocarme solo en aquellos módulos que son parte de mi paquete).cómo encontrar la lista de módulos que dependen de un módulo específico en python

La recarga de archivos individuales de python parece funcionar en algunos casos y no en otros. Supongo que todos los módulos que dependen de un módulo modificado deberían volver a cargarse y la recarga debería realizarse en el orden correcto.

Estoy buscando una forma de obtener la lista de módulos importados por un módulo específico. ¿Hay alguna manera de hacer este tipo de introspección en Python?

Entiendo que mi enfoque podría no estar 100% garantizado y la forma más segura sería volver a cargar todo, pero si un enfoque rápido funciona en la mayoría de los casos, sería suficiente para fines de desarrollo.

Respuesta a los comentarios respecto DJango autoreloader

@Glenn Maynard, Gracias, había leído sobre autoreloader de Django. Mi aplicación web se basa en Zope 3 y con la cantidad de paquetes y muchas inicializaciones basadas en ZCML, el reinicio total tarda de 10 segundos a 30 segundos o más si el tamaño de la base de datos es más grande. Estoy intentando reducir el tiempo que paso durante el reinicio. Cuando siento que he realizado muchos cambios, generalmente prefiero reiniciar por completo, pero con mayor frecuencia cambio algunas líneas aquí y allá para las que no deseo pasar tanto tiempo. La configuración de desarrollo es completamente independiente de la configuración de producción y, por lo general, si algo está mal en la recarga, se vuelve obvio ya que las páginas de la aplicación comienzan a mostrar información ilógica o arrojar excepciones. Estoy muy interesado en explorar si la recarga selectiva funcionaría o no.

+0

Es mucho más seguro hacer algo como el autoreloader de Django, que vuelve a ejecutar el backend por completo cuando se modifica un archivo de origen. No sé de ninguna desventaja; modificas un archivo y todo se vuelve a cargar uno o dos segundos más tarde, automáticamente. Algo que solo funciona en "la mayoría de los casos" es muy malo para el desarrollo; solo pides que te muerdan dolorosamente en el camino cuando no es así. –

+0

Revisando aquí debido a una pregunta duplicada, y agregando que "la manera de hacer minimizar el tiempo de recarga cuando se usa zope" es usar sauna.reload por ahora (2013) – jsbueno

Respuesta

5

Por lo tanto - que esto responde a "Encuentra una lista de módulos que dependen de una determinada uno "- en lugar de cómo se formuló inicialmente la pregunta - que respondí anteriormente.

Como resultado, esto es un poco más complejo: uno tiene que encontrar el árbol de dependencia para todos los módulos cargados, e invertirlo para cada módulo, conservando una orden de carga que no rompa las cosas.

También había publicado este pitón a la wiki del restaurante brasileño en: 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) 

Este wokred muy bien en todas las pruebas que he hecho aquí - pero no me recoment abusar de ella. Pero para actualizar un servidor zope2 en ejecución después de editar algunas líneas de código, creo que usaría esto yo mismo.

+0

Hola, me di cuenta de que solo buscaba los atributos ModuleType en el dir (módulo) no es suficiente. Varias veces las importaciones parecen '' de xyz import abc''. Para manejar esto, uno también debe considerar los atributos FunctionType y ClassType en el listado de dir (módulo) y para aquellos, uno debe recoger su getattr correspondiente (attr, '__module__') y agregarlos en las dependencias –

+0

De hecho, tendré que arreglar eso - o eliminar el código de ambos lugares - es tan convincente que ahora "debe" funcionar para quien lo necesite – jsbueno

+0

* FIJO * el ejemplo publicado. – jsbueno

3

Es posible que desee echar un vistazo a módulo Pega recargador de Ian Bicking, que hace lo que quiere ya:

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

No le da específicamente una lista de los archivos dependientes (que no es más que técnicamente posible si el empaquetador ha sido diligente y ha especificado adecuadamente las dependencias), pero si mira el código, obtendrá una lista precisa de los archivos modificados para reiniciar el proceso.

2

un poco de introspección al rescate:

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 

Esto le da un conjunto con todos los módulos cargados - acaba de llamar a la función con el primer módulo como parámetro único. A continuación, puede iterar sobre el conjunto resultante recarga del mismo, la forma más sencilla: [recarga (m) para m en find_modules (< módulo >)]

+0

Solo trato de entender este código. - comience con un módulo dado x - cree un conjunto vacío de módulos que hayan sido importados por x - iterado sobre dir (x) para identificar todos los elementos que sean módulos - agréguelos al conjunto de módulos para x - haga esto de forma recursiva para encontrar todas las dependencias para x Probablemente necesite comenzar con esto y obtener un mapeo inverso para identificar el conjunto de todos los módulos que dependen de un módulo específico –

+0

Gah, ¿por qué haría esto? sys.modules ya contiene todos los módulos cargados .... –

+0

Espera - ¿necesitas una "lista de módulos de los que depende un módulo en python" o una lista "de todos los módulos que dependen de un módulo específico"? Puedo encontrar un código para el último, no mucho más complejo que este, pero la pregunta está formulada para el primero. – jsbueno

Cuestiones relacionadas