检测循环进口

时间:2010-03-09 01:11:15

标签: python circular-dependency

我正在使用一个包含大约30个独特模块的项目。它设计得不太好,因此在向项目添加一些新功能时创建循环导入是很常见的。

当然,当我添加循环导入时,我不知道它。有时很明显,当我收到AttributeError: 'module' object has no attribute 'attribute'之类的错误时,我已经进行了循环导入,我明确定义了'attribute'。但其他时候,代码不会因为它的使用方式而抛出异常。

所以,对我的问题:

  

是否可以通过编程方式检测循环导入的发生时间和位置?

到目前为止,我能想到的唯一解决方案是让模块importTracking包含一个dict importingModules,一个函数importInProgress(file),它会增加importingModules[file],然后抛出如果它大于1则为错误,并且函数importComplete(file)递减importingModules[file]。所有其他模块看起来像:

import importTracking
importTracking.importInProgress(__file__)
#module code goes here.
importTracking.importComplete(__file__)

但这看起来真的很讨厌,必须有更好的方法去做,对吧?

4 个答案:

答案 0 :(得分:6)

为了避免更改每个模块,您可以将导入跟踪功能放在import hook中,或者放在自定义__import__中,您可以将其粘贴在内置插件中 - 后者,一次,可能会更好,因为__import__被调用,即使导入的模块已经在sys.modules,这是在循环导入期间的情况。

对于实现,我只是使用“在导入过程中”的一组模块,类似于(benjaoming edit:插入源自原始的工作片段):

beingimported = set()
originalimport = __import__
def newimport(modulename, *args, **kwargs):
    if modulename in beingimported:
        print "Importing in circles", modulename, args, kwargs
        print "    Import stack trace -> ", beingimported
        # sys.exit(1) # Normally exiting is a bad idea.
    beingimported.add(modulename)
    result = originalimport(modulename, *args, **kwargs)
    if modulename in beingimported:
        beingimported.remove(modulename)
    return result
import __builtin__
__builtin__.__import__ = newimport

答案 1 :(得分:1)

并非所有循环导入都是一个问题,正如您在未抛出异常时发现的那样。

当它们出现问题时,下次尝试运行任何测试时都会出现异常。发生这种情况时,您可以更改代码。

我认为这种情况不需要做任何改变。

什么时候不成问题的例子:

a.py

import b
a = 42
def f():
  return b.b

b.py

import a
b = 42
def f():
  return a.a

答案 2 :(得分:1)

import使用__builtin__.__import__(),因此如果你monkeypatch那么每个地方的所有导入都会获取更改。请注意,循环导入必然是一个问题。

答案 3 :(得分:1)

Python中的循环导入与PHP包含的不同。

Python导入的模块首次加载到导入“处理程序”中,并在此过程中保留。对于每个后续导入,此处理程序为本地命名空间中的名称分配从该模块导入的内容。模块是唯一的,对该模块名称的引用将始终指向相同的已加载模块,无论它在何处导入。

因此,如果您有循环模块导入,则每个文件的加载将发生一次,然后每个模块将具有与在其命名空间中创建的其他模块相关的名称。

当引用两个模块中的特定名称时当然会出现问题(当循环导入发生在相反模块的导入中引用的类/函数定义之前)时,如果出现这种情况,则会出现错误发生的情况。