为了减少基于Python的Web应用程序的开发时间,我试图对我最近修改过的模块使用reload()。 reload()通过专用网页(Web应用程序的开发版本的一部分)进行,该网页列出了最近修改过的模块(并且py文件的修改时间戳晚于相应的pyc文件)。完整的模块列表是从sys.modules获得的(我过滤列表只关注那些属于我的包的模块)。
重新加载单个python文件似乎在某些情况下有效,而在其他情况下则无效。我想,所有依赖于修改过的模块的模块都应该重新加载,重新加载应该按照正确的顺序进行。
我正在寻找获取特定模块导入的模块列表的方法。有没有办法在Python中进行这种内省?
我知道我的方法可能不是100%保证,最安全的方法是重新加载所有内容,但如果快速方法适用于大多数情况,那么它对于开发目的来说已经足够了。
对有关DJango自动加载器的评论的回应
@Glenn Maynard,Thanx,我读过关于DJango的自动重载器。我的Web应用程序基于Zope 3以及包的数量和大量基于ZCML的初始化,如果数据库大小更大,则总重启大约需要10秒到30秒或更长时间。我试图减少重启期间花费的这段时间。当我觉得我做了很多改变时,我通常更喜欢完全重启,但更多的时候我会改变这里和那里的几行,我不想花那么多时间。开发设置完全独立于生产设置,通常如果重新加载出现问题,由于应用程序页面开始显示不合逻辑的信息或抛出异常,因此很明显。我非常有兴趣探索选择性重载是否有效。
答案 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