代码之家  ›  专栏  ›  技术社区  ›  Shailesh Kumar

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

  •  10
  • Shailesh Kumar  · 技术社区  · 15 年前

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

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

    我正在寻找一种方法来获取特定模块导入的模块列表。在python中有什么方法可以做这种自省吗?

    我知道我的方法可能没有100%的保证,最安全的方法是重新加载所有内容,但是如果一个快速的方法适用于大多数情况,它就足以用于开发目的了。

    对有关django autoreloader的评论的回应

    @格伦·梅纳德,桑克斯,我读过德扬戈的自动装弹机。我的Web应用程序基于Zope3,有大量的包和许多基于ZCML的初始化,如果数据库大小更大,总重新启动大约需要10秒到30秒或更长时间。我正试图减少重新启动期间所花费的时间。当我觉得我已经做了很多更改时,我通常更喜欢完全重新启动,但更经常的是我在这里和那里更改了几行,我不想为此花费太多时间。开发设置完全独立于生产设置,通常如果在重新加载过程中发生了错误,那么从应用程序页面开始显示不合逻辑的信息或抛出异常开始,它就变得很明显。我非常有兴趣探索选择性重新加载是否有效。

    3 回复  |  直到 15 年前
        1
  •  5
  •   jsbueno    15 年前

    所以-这个答案“找到一个依赖于给定模块的模块列表”-而不是问题的最初措辞-我在上面回答了这个问题。

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

    我也在巴西的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服务器,我想我自己也会使用它。

        2
  •  3
  •   Douglas Mayle    15 年前

    你可能想看看伊恩·比金的粘贴重新加载模块,它已经实现了你想要的功能:

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

    它并没有给您一个特定的依赖文件列表(这在技术上是可能的,如果包装商已经努力和适当指定的依赖项),但查看代码将给您一个准确的修改文件列表,以重新启动进程。

        3
  •  2
  •   jsbueno    15 年前

    对救援的一些反思:

    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
    

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