如何调试被覆盖的 sys.modules 条目?

How to debug a sys.modules entry that gets overwritten?

我正在尝试调试导致 sys.modules['numpy'] 被覆盖的问题。我在 numpy.__init__ 中添加了一些打印语句,当我尝试导入 numpy 时,我得到了这个输出:

numpy.__init__ running
id(sys.modules) = 89034704
id(sys.modules['numpy']) = 161528304
numpy.__init__ running
id(sys.modules) = 89034704
id(sys.modules['numpy']) = 177135864

Numpy 有许多循环导入,它们应该按照 this answer 中的描述工作。但在我的例子中,不是从 sys.modules 获取部分初始化的 numpy 模块,而是再次导入 numpy,然后 numpy.__init__ 第二次执行,导致崩溃。

我如何检测 sys.modules 以了解谁在何时覆盖 sys.modules['numpy'] 通常我会编写一个 dict 子类,但我不要认为将 sys.modules 更改为指向我自己的对象是安全的。我尝试覆盖 sys.modules.__setattr__,但这是一个只读属性。

上下文:我正在尝试在 Julia 库 PyCall 中调试 this issue。 PyCall 在运行的 Julia 进程中嵌入了一个 Python 解释器,并将导入委托给 PyImport_ImportModule from cpython。上面的问题发生在对 PyImport_ImportModule 的一次调用中,所以我希望这个问题应该在 python / cpython 的知识下回答,但不了解 Julia / PyCall。

您可以将 sys.modules 从普通 dict 更改为 prints 输出作业,例如:

import sys
import traceback

class noisydict(dict):
    def __setitem__(self, key, value):
        print('ASSIGNED: key={!r} value={!r} at:'.format(key, value))
        traceback.print_stack()
        return dict.__setitem__(self, key, value)

sys.modules = noisydict(sys.modules)

如果覆盖发生在 C 代码中,这可能会或可能不会工作(此类代码可能会直接访问底层 dict.__setitem__ 而不是像 Python 代码那样只执行 sys.modules[name] = newmodule)但值得一试!

感谢@BrenBarn 将我指向 。以下内容适用于我的目的:

importhack.py:

import traceback

old_import = __import__

def my_import(module, *args, **kwargs):
    print "my_import({}) caused by:".format(module)
    traceback.print_stack()
    return old_import(module, *args, **kwargs)

__builtins__['__import__'] = my_import

用法:

>>> import importhack
>>> import numpy

我相信 PyCall.jl 中的原始问题是由在 Python 解释器完全初始化之前调用 PyImport_ImportModule 引起的。