The following script will run a module and break into post-mortem debugging if an exception occurs while running the module. It should work both with Python 2.7 and 3.x.
Usage:
mdb.py module_name [args ...]
Known limitations:
- While running the module code,
sys.argv[0]
is preserved as the module name, instead of being resolved to the file path of the module.
- If the target module is not found, the error is not reported any differently than if the error occurred during the execution of the 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])
Demonstration:
$ 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