debugging 跟踪Python导入

twh00eeo  于 2023-02-09  发布在  Python
关注(0)|答案(3)|浏览(140)

我的Python库刚刚将它的主模块名从foo.bar改为foobar。对于向后compat,foo.bar仍然存在,但是导入它会引发一些警告。现在,看起来一些示例程序仍然从旧模块导入,但不是直接导入。
我想找到错误的import语句。有没有什么工具可以让我跟踪导入并找到罪魁祸首,而不必费力地通过所有的代码?

h5qlskok

h5qlskok1#

-v启动python解释器:

$ python -v -m /usr/lib/python2.6/timeit.py
# installing zipimport hook
import zipimport # builtin
# installed zipimport hook
# /usr/lib/python2.6/site.pyc matches /usr/lib/python2.6/site.py
import site # precompiled from /usr/lib/python2.6/site.pyc
# /usr/lib/python2.6/os.pyc matches /usr/lib/python2.6/os.py
import os # precompiled from /usr/lib/python2.6/os.pyc
import errno # builtin
import posix # builtin
# /usr/lib/python2.6/posixpath.pyc matches /usr/lib/python2.6/posixpath.py
import posixpath # precompiled from /usr/lib/python2.6/posixpath.pyc
# /usr/lib/python2.6/stat.pyc matches /usr/lib/python2.6/stat.py
import stat # precompiled from /usr/lib/python2.6/stat.pyc
# /usr/lib/python2.6/genericpath.pyc matches /usr/lib/python2.6/genericpath.py
import genericpath # precompiled from /usr/lib/python2.6/genericpath.pyc
# /usr/lib/python2.6/warnings.pyc matches /usr/lib/python2.6/warnings.py
import warnings # precompiled from /usr/lib/python2.6/warnings.pyc
# /usr/lib/python2.6/linecache.pyc matches /usr/lib/python2.6/linecache.py
import linecache # precompiled from /usr/lib/python2.6/linecache.pyc
# /usr/lib/python2.6/types.pyc matches /usr/lib/python2.6/types.py
import types # precompiled from /usr/lib/python2.6/types.pyc
# /usr/lib/python2.6/UserDict.pyc matches /usr/lib/python2.6/UserDict.py
...

然后只对旧模块执行grep。

m2xkgtsf

m2xkgtsf2#

编辑foo.bar模块,添加以下代码:

import pdb
pdb.set_trace()

当foo.bar被导入时,程序将在pdb模式下停止在pdb.set_trace()处,在那里您可以调试您的代码。例如,您可以使用“w”命令打印完整的调用堆栈。

k10s72fa

k10s72fa3#

我将首先介绍一般的解决方案,然后展示如何将其调整为适合于这个特定的用例。

通用溶液

现在有更简单的方法来调试这些东西,利用python导入系统的新功能。基本上只需添加你自己的模块查找器(MetaPathFinder)到**sys.meta_path**。下面是一个导入pandas的脚本示例,列出了imported中所有成功的导入和could_not_be_imported中所有不成功的导入:

import sys

imported = []
could_not_be_imported = []

class FirstFinder:
    def find_spec(self, modulename, path=None, target=None):
        imported.append(modulename)

class LastFinder:
    def find_spec(self, modulename, path=None, target=None):
        imported.remove(modulename)
        could_not_be_imported.append(modulename)

# setup ("start recording imports")
sys.meta_path.insert(0, FirstFinder())
sys.meta_path.append(LastFinder())

import pandas # import anything here 

# cleanup ("stop recording")
sys.meta_path = [
    x for x in sys.meta_path if not isinstance(x, (FirstFinder, LastFinder))
]

包含成功导入的列表为**imported**:

>>> imported
['pandas',
 'numpy',
 'numpy._globals',
 # ...
 'pandas.io.sql',
 'pandas.io.stata',
 'pandas.io.xml',
 'pandas.util._tester',
 'pandas._version']

和**could_not_be_imported**:

>>> could_not_be_imported 
['pickle5',
 'org',
 'fcntl',
 'backports_abc',
 'six.moves',
 'backports_abc',
 'backports_abc',
 # ...
 'cloudpickle',
 'numexpr',
 'bottleneck',
 'org',
 'backports_abc',
 'backports_abc',
 'backports_abc']

:两个列表中可能存在重复项,所有模块均按导入顺序列出。请根据需要进行修改。

特定用例:查找错误导入

LastFinder修改为:

class LastFinder:
    def find_spec(self, modulename, path=None, target=None):
        imported.remove(modulename)
        print(
            "\nMissing module! ",
            f"Tried to import: {modulename}\n",
            "Last few last imports:\n\t",
            "\n\t ".join(imported[-10:]),
        )
        could_not_be_imported.append(modulename)

然后,它将打印每个丢失的模块,以及最近几个成功导入的模块和您试图导入的模块的列表。例如:

Missing module!  Tried to import: bottleneck
 Last few last imports:
         pandas.core.ops.common
         pandas.core.ops.docstrings
         pandas.core.ops.mask_ops
         pandas.core.ops.methods
         pandas.core.missing
         pandas.core.array_algos.quantile
         pandas.core.sorting
         pandas.core.arrays.boolean
         pandas.core.arrays.masked
         pandas.core.nanops

在这种情况下,这意味着pandas.core.nanops是不成功导入之前的最后一个成功导入。因此,从pandas.core.nanops.__file__检查中断导入的位置将非常容易。(在文件中查找"bottleneck")。在我的示例中,我发现:

# pandas\core\nanops.py
# ...
bn = import_optional_dependency("bottleneck", errors="warn")

这是如何工作的?

python导入系统遍历sys.meta_path来寻找一个spec finder,这个spec finder有find_spec方法返回None以外的东西,如果返回了None,那么sys.meta_path中的下一个finder将被用于导入。

相关问题