如何从命令行调试使用python -m运行的Python模块?

问题描述 投票:24回答:6

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

python -m pdb my_script.py

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

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

python -m pdb -m my_module
python debugging python-module pdb
6个回答
15
投票

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

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

这意味着要解释其余参数列表是mod's >>工作,并且此行为完全取决于mod在内部的设计方式以及是否支持其他-m

让我们检查python 2.x

pdb内部发生了什么。实际上,没有什么可解释的,它只希望提供一个脚本名称:
   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

的版本相同>>

好消息

允许执行您要问的请求的拉取请求已经在merged于5天之前了。多么神秘的巧合!这是code

因此,请稍等一下即将到来的python 3.x版本,以解决此问题)

以下脚本将运行一个模块,如果在运行该模块时发生异常,则会进入验尸调试。它应同时适用于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

Python 3.7添加了该功能

the docs,看起来您的命令:

python -m pdb -m my_module

将开始在Python 3.7上工作:

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

您可以在要调试的代码之前,在代码中添加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调试器。

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

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

鉴于此,按照您的第一个示例,通过运行.py文件,我将充满信心地进行调试。要记住的一件事是-m搜索sys.path。幸运的是,python首先会查看当前的工作目录,因此,只要要调试的.py位于cwd中,就相当于python -m modulepython module.py

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

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

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

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

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

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


4
投票

以下脚本将运行一个模块,如果在运行该模块时发生异常,则会进入验尸调试。它应同时适用于Python 2.7和3.x。


4
投票

Python 3.7添加了该功能


3
投票

您可以在要调试的代码之前,在代码中添加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

2
投票

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

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


0
投票

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

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

© www.soinside.com 2019 - 2024. All rights reserved.