【问题标题】:How to debug a Python module run with python -m from the command line?如何从命令行调试使用 python -m 运行的 Python 模块?
【发布时间】:2018-02-26 04:42:46
【问题描述】:

我知道可以从命令行调试 Python 脚本

python -m pdb my_script.py

如果my_script.py 是一个打算与python my_script.py 一起运行的脚本。

但是,python 模块my_module.py 应该与python -m my_module 一起运行。即使是包含相对导入的脚本也应该使用python -m 运行。如何在pdb 的控制下运行python -m my_module?以下不起作用

python -m pdb -m my_module

【问题讨论】:

    标签: python debugging python-module pdb


    【解决方案1】:

    你现在不能这样做,因为-m 终止了选项列表

    python -h
    ...
    -m mod : run library module as a script (terminates option list)
    ...
    

    这意味着 mod 的工作是解释参数列表的其余部分,这种行为完全取决于 mod 的内部设计方式以及它是否支持另一个 -米

    让我们看看 python 2.xpdb 内部发生了什么。实际上,没有什么有趣的,它只需要提供一个脚本名称:

       if not sys.argv[1:] or sys.argv[1] in ("--help", "-h"):
            print "usage: pdb.py scriptfile [arg] ..."
            sys.exit(2)
    
        mainpyfile =  sys.argv[1]     # Get script filename
        if not os.path.exists(mainpyfile):
            print 'Error:', mainpyfile, 'does not exist'
            sys.exit(1)
    
        del sys.argv[0]         # Hide "pdb.py" from argument list
    
        # Replace pdb's dir with script's dir in front of module search path.
        sys.path[0] = os.path.dirname(mainpyfile)
    
        # Note on saving/restoring sys.argv: it's a good idea when sys.argv was
        # modified by the script being debugged. It's a bad idea when it was
        # changed by the user from the command line. There is a "restart" command
        # which allows explicit specification of command line arguments.
        pdb = Pdb()
        while True:
            try:
                pdb._runscript(mainpyfile)
    

    python 3.x

    的当前发布版本也是如此

    好消息

    5 天前,允许执行您所要求的请求的拉取请求是 merged。多么神秘的巧合!这是code

    所以请稍等一下,等待即将发布的 python 3.x 版本解决此问题)

    【讨论】:

    • 我为您提供了合并代码参考的赏金。认为@Leon 也应该得到一些,但不知道如何拆分它。他确实为以前的版本提供了解决方案。这似乎超出了问题范围,但这两种解决方案似乎都暗示新模块没有 CLI 参数。
    • 请参阅my answer,了解适用于旧 Python 版本的更轻量级的解决方案。
    【解决方案2】:

    Python 3.7 添加了该功能

    来自the docs,看起来你的命令:

    python -m pdb -m my_module
    

    将开始使用 Python 3.7:

    3.7 版中的新功能:pdb.py 现在接受一个 -m 选项,该选项类似于 python3 -m 执行模块的方式。与脚本一样,调试器将在模块的第一行之前暂停执行。

    【讨论】:

    • @Xaser 如果你有的话,你能解释一下如何解决吗?
    【解决方案3】:

    如果在运行模块时发生异常,以下脚本将运行模块并进入事后调试。它应该适用于 Python 2.7 和 3.x。

    用法

    mdb.py module_name [args ...]
    

    已知限制

    • 在运行模块代码时,sys.argv[0] 被保留为模块名称,而不是解析为模块的文件路径。
    • 如果未找到目标模块,则报告错误与在执行模块期间发生错误时没有任何不同

    mdb.py

    #!/usr/bin/env python
    
    from __future__ import print_function
    import pdb
    import runpy
    import sys
    import traceback
    
    if len(sys.argv) == 0:
        print("Usage: mdb.py module_name [args ...]")
        exit(1)
    
    modulename = sys.argv[1]
    del sys.argv[0]
    
    try:
        runpy.run_module(modulename, run_name='__main__')
    except:
        traceback.print_exception(*sys.exc_info())
        print("")
        print("-" * 40)
        print("mdb: An exception occurred while executing module ", modulename)
        print("mdb: See the traceback above.")
        print("mdb: Entering post-mortem debugging.")
        print("-" * 40)
        pdb.post_mortem(sys.exc_info()[2])
    

    演示

    $ tree
    .
    ├── mdb.py
    └── mypackage
        ├── __init__.py
        ├── __main__.py
        └── mymodule.py
    
    1 directory, 4 files
    
    $ ###################### Examine the module code ###################
    $ cat mypackage/mymodule.py 
    from __future__ import print_function
    import sys
    
    print("mymodule loaded")
    
    if __name__ == "__main__":
        print("mymodule executed")
        print("args:", sys.argv)
    
    $ #################### Run the module through python ###############
    $ python -m mypackage.mymodule abc defgh
    mymodule loaded
    mymodule executed
    args: ['/home/leon/playground/mdb/mypackage/mymodule.py', 'abc', 'defgh']
    
    $ #################### Run the module through mdb ##################
    $ ./mdb.py mypackage.mymodule abc defgh
    mymodule loaded
    mymodule executed
    args: ['mypackage.mymodule', 'abc', 'defgh']
    $ ###   ^^^^^^^^^^^^^^^^^^
    $ ### Note that sys.argv[0] is not resolved to the file path
    
    $ ###################### Examine the module code ###################
    $ cat mypackage/__main__.py 
    from __future__ import print_function
    import sys
    
    print("mypackage loaded")
    
    if __name__ == "__main__":
        print("mypackage executed")
        print("args:", sys.argv)
        print(x + y)
    
    $ #################### Run the module through python ###############
    $ python -m mypackage
    mypackage loaded
    mypackage executed
    args: ['/home/leon/playground/mdb/mypackage/__main__.py']
    Traceback (most recent call last):
      File "/usr/lib/python2.7/runpy.py", line 174, in _run_module_as_main
        "__main__", fname, loader, pkg_name)
      File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
        exec code in run_globals
      File "/home/leon/playground/mdb/mypackage/__main__.py", line 9, in <module>
        print(x + y)
    NameError: name 'x' is not defined
    
    $ #################### Run the module through mdb ##################
    $ ./mdb.py mypackage
    mypackage loaded
    mypackage executed
    args: ['mypackage']
    Traceback (most recent call last):
      File "./mdb.py", line 17, in <module>
        runpy.run_module(modulename, run_name='__main__')
      File "/usr/lib/python2.7/runpy.py", line 192, in run_module
        fname, loader, pkg_name)
      File "/usr/lib/python2.7/runpy.py", line 72, in _run_code
        exec code in run_globals
      File "/home/leon/playground/mdb/mypackage/__main__.py", line 9, in <module>
        print(x + y)
    NameError: name 'x' is not defined
    
    ----------------------------------------
    mdb: An exception occurred while executing module  mypackage
    mdb: See the traceback above.
    mdb: Entering post-mortem debugging.
    ----------------------------------------
    > /home/leon/playground/mdb/mypackage/__main__.py(9)<module>()
    -> print(x + y)
    (Pdb) q
    

    【讨论】:

    • 这意味着一个没有任何cli参数的模块,对吧?
    • @TheMeaningfulEngineer 是的,有,但现在没有
    【解决方案4】:

    您可以在要调试的代码之前在代码中添加pdb.set_trace() 以进行交互式调试。

    class C:    
        def __init__(self, x):
            self.x = x
    
        def inst_f(self):
            pass
    
    a = C('this is a')
    import pdb
    pdb.set_trace()
    b = C('this is b')
    
    print a.x is b.x
    

    运行会输出

    > c:\python27\tests\test.py(11)<module>()
    -> b = C('this is b')
    (Pdb) 
    

    并且让你使用python调试器。

    【讨论】:

    • 我想知道是否/如何在不编辑源代码的情况下从命令行启动调试器。
    【解决方案5】:

    正如其他人所说,此功能是在 Python 3.7 中添加的。但是,人们仍在使用旧版本,因此希望下面的解决方案可以帮助这些人!

    基本上你可以通过导入包的__main__.py文件来创建一个运行模块的脚本:

    # runner.py
    import path.to.my.module.__main__
    

    然后只需在 pdb 下运行该脚本:python -m pdb runner.py [args]

    此解决方案适用于相对导入(因为 __main__ 在包路径下运行,而不是作为脚本调用)。

    之所以只需要导入__main__模块,是因为Python导入会触发顶层代码执行,而__main__中的逻辑是在顶层执行的。

    【讨论】:

      【解决方案6】:

      根据python 命令行手册页,-m 标志执行以下操作:

      在 sys.path 中搜索命名模块并将相应的 .py 文件作为脚本运行。

      鉴于此,我会根据您的第一个示例运行 .py 文件进行调试。要记住的一件事是 -m 搜索 sys.path。幸运的是,python 首先查看当前工作目录,因此只要您正在调试的 .py 在您的 cwd 中,python -m modulepython module.py 等效。

      【讨论】:

      • 如果一个文件包含相对导入,如果没有-m运行会产生错误。无论如何,我认为执行python -m my_package而不是python my_package/__main__.py时运行时环境有些不同。
      • 正确,请参阅my answer 了解解决方法。
      猜你喜欢
      • 2014-01-11
      • 1970-01-01
      • 2023-03-23
      • 2019-04-15
      • 1970-01-01
      • 1970-01-01
      • 2023-01-14
      • 1970-01-01
      • 2020-05-05
      相关资源
      最近更新 更多