【问题标题】:how to find list of modules which depend upon a specific module in python如何查找依赖于python中特定模块的模块列表
【发布时间】:2010-12-22 02:35:16
【问题描述】:

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

重新加载单个 python 文件似乎在某些情况下有效,而在其他情况下无效。我想,所有依赖于修改模块的模块都应该重新加载,并且重新加载应该以正确的顺序发生。

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

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

就 DJango autoreloader 对 cme​​ts 的回应

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

【问题讨论】:

  • 执行 Django 的 autoreloader 之类的东西要安全得多,它会在修改源文件时完全重新执行后端。我不知道有什么缺点;你修改了一个文件,一切都会在一两秒后自动重新加载。仅在“大多数”情况下有效的东西对开发非常不利;你只是要求在没有被咬的时候被痛苦地咬。
  • 由于一个重复的问题而重新访问这里,并补充说“使用 zope 时尽量减少重新加载时间的方法”是现在使用sanana.reload (2013)

标签: python


【解决方案1】:

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

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

我还把这个发布到了巴西的 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 服务器,我想我会自己使用它。

【讨论】:

  • 嗨,我发现仅仅在 dir(module) 中查找 ModuleType 属性是不够的。很多时候导入看起来像from xyz import abc。为了解决这个问题,还应该考虑 dir(module) 列表中的 FunctionType 和 ClassType 属性,对于这些属性,应该获取它们对应的 getattr(attr, 'module') 并将它们添加到依赖项中
  • 确实如此。我将不得不解决这个问题 - 或者从两个地方删除代码 - 现在它是如此复杂,它“必须”为任何需要它的人工作。
  • ClassType 不再是 3.4 中的类型(可能更早),可以安全地假设 MethodType 将是它的替代品吗?
【解决方案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

这为您提供了一个包含所有已加载模块的集合 - 只需使用您的第一个模块作为唯一参数调用该函数。然后,您可以迭代重新加载它的结果集,就像: [reload (m) for m in find_modules()]

【讨论】:

  • 只是想理解这段代码。 - 从给定的模块 x 开始 - 创建一个由 x 导入的空模块集 - 遍历 dir(x) 以识别所有恰好是模块的项目 - 将它们添加到 x 的模块集 - 递归执行此操作要找到 x 的所有依赖项,我可能需要从这个开始并完成反向映射以识别依赖于特定模块的所有模块的集合
  • 等等——你需要一个“模块在python中依赖的模块列表”还是一个“依赖于特定模块的所有模块的列表”?我可以为后者提供代码,并不比这个复杂得多 - 但问题是针对前者提出的。
  • @jsbueno 是的,你说得对,我的主题行用错了 :( 我现在已经修改了主题行。我正在寻找依赖于特定模块的所有模块。
【解决方案3】:

您可能想看看 Ian Bicking 的 Paste reloader 模块,它已经完成了您想要的工作:

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

它并没有专门为您提供依赖文件的列表(这在技术上只有在打包程序已经勤奋并正确指定依赖项时才可行),但查看代码将为您提供修改文件的准确列表以重新启动过程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-02-01
    • 1970-01-01
    • 2010-09-14
    • 2015-03-16
    • 2020-02-05
    • 2021-01-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多