tutorial - ¿Cómo depurar un módulo de Python ejecutado con python-m desde la línea de comandos?
the django project (5)
Sé que una secuencia de comandos de Python se puede depurar desde la línea de comandos con
python -m pdb my_script.py
si my_script.py
es un script destinado a ejecutarse con python my_script.py
.
Sin embargo, un módulo python my_module.py
debe ejecutarse con python -m my_module
. Incluso los scripts que contienen importaciones relativas deben ejecutarse con python -m
. ¿Cómo puedo ejecutar python -m my_module
bajo el control de pdb
? Lo siguiente no funciona :
python -m pdb -m my_module
De acuerdo con la página de manual de la línea de comandos de python
, el indicador -m hace lo siguiente:
Busca en sys.path el módulo nombrado y ejecuta el archivo .py correspondiente como un script.
Dado esto, me sentiría confiado en la depuración ejecutando el archivo .py según su primer ejemplo. Una cosa a tener en cuenta es que -m busca en sys.path
. Afortunadamente, python primero mira el directorio de trabajo actual, de modo que siempre que .py esté depurando esté en su cwd, python -m module
y python module.py
equivalente.
La siguiente secuencia de comandos ejecutará un módulo y se dividirá en la depuración post mortem si se produce una excepción al ejecutar el módulo. Debería funcionar tanto con Python 2.7 como con 3.x.
Uso
mdb.py module_name [args ...]
Limitaciones conocidas :
- Mientras se ejecuta el código del módulo,
sys.argv[0]
se conserva como el nombre del módulo, en lugar de resolverse en la ruta del archivo del módulo. - Si no se encuentra el módulo de destino, el error no se reporta de manera diferente que si el error ocurrió durante la ejecución del módulo
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])
Demostración :
$ 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
No puedes hacerlo ahora, porque -m
termina la lista de opciones
python -h
...
-m mod : run library module as a script (terminates option list)
...
Eso significa que el trabajo de mod es interpretar el resto de la lista de argumentos y este comportamiento depende completamente de cómo se diseñe internamente y de si es compatible con otro -m
Echemos un vistazo a lo que está pasando dentro de pdb de python 2.x. En realidad, nada interesante, solo espera que se proporcione un nombre de script:
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)
Lo mismo para las versiones lanzadas actualmente de Python 3.x
Buenas noticias
La solicitud de extracción que permite hacer lo que está pidiendo se ha merged 5 días. ¡Qué misteriosa coincidencia! Aquí está el code
Así que solo espere un poco para que las próximas versiones de Python 3.x tengan este problema resuelto)
Puede agregar pdb.set_trace()
en su código para la depuración interactiva, antes del código que desea depurar.
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
Ejecutando esto saldrá
> c:/python27/tests/test.py(11)<module>()
-> b = C(''this is b'')
(Pdb)
Y te deja usar el depurador de Python.
Python 3.7 agrega esa característica
De los documentos, parece que su comando:
python -m pdb -m my_module
comenzará a trabajar en Python 3.7:
Nuevo en la versión 3.7: pdb.py ahora acepta una opción -m que ejecuta módulos de manera similar a como lo hace python3 -m. Al igual que con un script, el depurador pausará la ejecución justo antes de la primera línea del módulo.