如何查找依赖于python中特定模块的模块列表

时间:2009-12-01 17:18:36

标签: python

为了减少基于Python的Web应用程序的开发时间,我试图对我最近修改过的模块使用reload()。 reload()通过专用网页(Web应用程序的开发版本的一部分)进行,该网页列出了最近修改过的模块(并且py文件的修改时间戳晚于相应的pyc文件)。完整的模块列表是从sys.modules获得的(我过滤列表只关注那些属于我的包的模块)。

重新加载单个python文件似乎在某些情况下有效,而在其他情况下则无效。我想,所有依赖于修改过的模块的模块都应该重新加载,重新加载应该按照正确的顺序进行。

我正在寻找获取特定模块导入的模块列表的方法。有没有办法在Python中进行这种内省?

我知道我的方法可能不是100%保证,最安全的方法是重新加载所有内容,但如果快速方法适用于大多数情况,那么它对于开发目的来说已经足够了。

对有关DJango自动加载器的评论的回应

@Glenn Maynard,Thanx,我读过关于DJango的自动重载器。我的Web应用程序基于Zope 3以及包的数量和大量基于ZCML的初始化,如果数据库大小更大,则总重启大约需要10秒到30秒或更长时间。我试图减少重启期间花费的这段时间。当我觉得我做了很多改变时,我通常更喜欢完全重启,但更多的时候我会改变这里和那里的几行,我不想花那么多时间。开发设置完全独立于生产设置,通常如果重新加载出现问题,由于应用程序页面开始显示不合逻辑的信息或抛出异常,因此很明显。我非常有兴趣探索选择性重载是否有效。

3 个答案:

答案 0 :(得分:5)

所以 - 这回答“查找依赖于给定模块的模块列表” - 而不是问题是如何初步措辞 - 我在上面回答。

事实证明,这有点复杂:必须为所有加载的模块找到依赖关系树,并为每个模块反转它,同时保留不会破坏事物的加载顺序。

我还把它发布到了brazillian的python wiki: 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)

这在我在这里做的所有测试中都很好 - 但是我不会反复滥用它。 但是为了在编辑几行代码后更新正在运行的zope2服务器,我想我会自己使用它。

答案 1 :(得分:3)

你可能想看一下Ian Bicking的Paste重新加载器模块,它可以做你想要的:

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

它没有特别为您提供一个依赖文件列表(只有在技术上可行的情况下,如果打包器已经是勤奋且正确指定的依赖项),但查看代码将为您提供一个准确的修改文件列表,以便重新启动过程

答案 2 :(得分:2)

对救援的一些反省:

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

这为您提供了一个包含所有已加载模块的集合 - 只需将第一个模块作为唯一参数调用该函数。然后,您可以迭代重新加载它的结果集,如下所示: [在find_modules(< module>)中重新加载(m)m