web-dev-qa-db-fra.com

Comment déboguer un module Python exécuté avec python -m à partir de la ligne de commande)?

Je sais qu'un script Python peut être débogué à partir de la ligne de commande avec

python -m pdb my_script.py

si my_script.py est un script destiné à être exécuté avec python my_script.py.

Cependant, un module python my_module.py doit être exécuté avec python -m my_module. Même les scripts contenant des importations relatives doivent être exécutés avec python -m. Comment puis-je exécuter python -m my_module sous le contrôle de pdb? Ce qui suit ne fonctionne pas:

python -m pdb -m my_module
24
Alexey

Vous ne pouvez pas le faire maintenant, car -m termine la liste d'options

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

Cela signifie que c'est le travail du mod d'interpréter le reste de la liste des arguments et ce comportement dépend entièrement de la façon dont mod est conçu en interne et s'il en supporte un autre - m

Permet de vérifier ce qui se passe à l'intérieur pdb of python 2.x . En fait, rien d'intéressant, il attend seulement qu'un nom de script soit fourni:

   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)

Idem pour les versions actuellement publiées de python 3.x

Bonnes nouvelles

La demande de tirage qui permet de faire ce que vous demandez a été fusionnée il y a 5 jours. Quelle mystérieuse coïncidence! Voici le code

Attendez donc un peu les prochaines versions python 3.x pour résoudre ce problème)

15
ffeast

Le script suivant exécutera un module et entrera dans le débogage post-mortem si une exception se produit lors de l'exécution du module. Il devrait fonctionner à la fois avec Python 2.7 et 3.x.

tilisation:

mdb.py module_name [args ...]

Limitations connues:

  • Lors de l'exécution du code du module, sys.argv[0] est conservé comme nom de module, au lieu d'être résolu dans le chemin de fichier du module.
  • Si le module cible n'est pas trouvé, l'erreur n'est pas signalée différemment que si l'erreur s'est produite lors de l'exécution du module

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])

Démonstration:

$ 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
4
Leon

Vous pouvez ajouter pdb.set_trace() dans votre code pour le débogage interactif, avant le code que vous souhaitez déboguer.

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

L'exécution de ceci produira

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

Et laissez-vous utiliser python débogueur.

3
Chen A.

Python 3.7 ajoute cette fonctionnalité

D'après les docs , il semble que votre commande:

python -m pdb -m my_module

va commencer à travailler sur Python 3.7:

Nouveau dans la version 3.7: pdb.py accepte désormais une option -m qui exécute des modules similaires à la façon dont python3 -m le fait. Comme avec un script, le débogueur suspendra l'exécution juste avant la première ligne du module.

Selon la page de manuel de ligne de commande python, l'indicateur -m effectue les opérations suivantes:

Recherche sys.path pour le module nommé et exécute le fichier .py correspondant en tant que script.

Compte tenu de cela, je me sentirais confiant en déboguant en exécutant le fichier .py selon votre premier exemple. Une chose à garder à l'esprit est que -m recherche sys.path. Heureusement, python regarde d'abord le répertoire de travail actuel, donc tant que le .py que vous déboguez est dans votre cwd, python -m module et python module.py équivalent.

2
dpiron