Как отладить модуль Python, запущенный с python -m из командной строки?

Я знаю, что Python script можно отлаживать из командной строки с помощью

python -m pdb my_script.py

если my_script.py является script, предназначенным для запуска с python my_script.py.

Однако, python module my_module.py должен запускаться с python -m my_module. Даже сценарии, содержащие относительный импорт, должны выполняться с python -m. Как я могу запустить python -m my_module под управлением pdb? Следующая не работает:

python -m pdb -m my_module

Ответы

Ответ 1

Вы не можете сделать это сейчас, потому что -m завершает список опций

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

Это означает, что это mod задание для интерпретации остальной части списка аргументов, и это поведение полностью зависит от того, как mod разработан внутри и поддерживает ли он другой м

Давайте посмотрим, что происходит внутри pdb python 2.x. На самом деле, ничего интересного, он ожидает только script name:

   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 dir with script dir in front of module search path.
    sys.path[0] = os.path.dirname(mainpyfile)

    # Note on saving/restoring sys.argv: it a good idea when sys.argv was
    # modified by the script being debugged. It 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

Хорошие новости

Запрос на pull, который позволяет делать то, что вы просите, был слит 5 дней назад. Какое таинственное совпадение! Здесь code

Так что просто подождите немного для предстоящих версий python 3.x, чтобы решить эту проблему.

Ответ 2

Следующий script будет запускать модуль и разбиваться на посмертную отладку, если во время работы модуля возникает исключение. Он должен работать как с 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

Ответ 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

Запуск этого приведет к выводу

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

И пусть вы используете отладчик Python.

Ответ 4

В соответствии с man-страницей командной строки python флаг -m выполняет следующие действия:

Ищет sys.path для именованного модуля и запускает соответствующий .py файл как script.

Учитывая это, я бы чувствовал уверенную отладку, запустив файл .py в соответствии с вашим первым примером. Следует иметь в виду, что -m ищет sys.path. К счастью, python сначала обращается к текущему рабочему каталогу, поэтому до тех пор, пока вы отлаживаете .py, вы используете эквивалент cwd, python -m module и python module.py.

Ответ 5

Python 3.7 добавляет эту функцию

Из документации видно, что ваша команда:

python -m pdb -m my_module

начну работать на Python 3.7:

Новое в версии 3.7: теперь pdb.py принимает опцию -m, которая выполняет модули, аналогично тому, как это делает python3 -m. Как и в случае со скриптом, отладчик приостанавливает выполнение непосредственно перед первой строкой модуля.