Reputation: 1091
When developing a Python package, it's very convenient to use the -m
option to run modules inside the package as scripts for quick testing. For example, for somepackage
with module somemodule.py
inside it, invoking
python -m somepackage.somemodule
from the directory where somepackage
resides will run somemodule.py
as though the submodule were __main__
. Using this calling syntax is especially important if the package is using explicit relative imports as described here.
Similarly, it is also convenient to use the -m
option to debug a script, as in
python -m pdb somescript.py
Is there any way to do both at the same time? That is, can I call a module as though it were a script and simultaneously launch into the debugger? I realize I can go into the code itself and insert import pdb; pdb.set_trace()
where I want to break, but I'm trying to avoid that.
Upvotes: 30
Views: 5295
Reputation: 115
This worked for me (debug python module as a script with -m option)
I created a scratch
import runpy
if __name__ == '__main__':
runpy.run_module('somepackage.somemodule', run_name="__main__", alter_sys=True)
idea taken from: Intellij/Pycharm can't debug Python modules
Upvotes: 0
Reputation: 42659
There are efforts underway to solve this in Python itself. Looks like with Python 3.7, you can do:
python -m pdb -m somepackage.somemodule
And I've provided a backport for older Python versions (2.7+):
pip install backports.pdb
python -m backports.pdb -m somepackage.somemodule
Upvotes: 29
Reputation: 42659
Building on @jed's answer, I built this module:
import pdb
import runpy
import sys
def main():
module = sys.argv[1]
sys.argv[1:] = sys.argv[2:]
pdb.runcall(runpy.run_module, module, run_name='__main__')
__name__ == '__main__' and main()
Put that module as mpdb.py
anywhere in your Python Path (current directory works), then you may invoke:
python -m mpdb somepackage.somemodule even with args
Upvotes: 3
Reputation: 580
Here's another option that also works with command line arguments.
It's generally a good idea to wrap your script's logic in a main
function. You can then have main
take in an optional list of arguments to override sys.argv
. Here's an example called argdemo.py
:
def main(cmd_line_args=None):
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("number", help="a number", type=int)
# allow cmd_line_args to override sys.argv
if cmd_line_args is None:
args = parser.parse_args()
else:
args = parser.parse_args(cmd_line_args)
print("The number is {}".format(args.number))
if __name__ == '__main__':
main()
This module can be run as usual:
$ python -m argdemo 2
> The number is 2
Or it can be run with pdb
by calling main()
directly:
$ python -c "import pdb; import argdemo; pdb.runcall(argdemo.main, ['2'])"
(Pdb) continue
> The number is 2
(Notice that cmd_line_args
has to be a list of strings just like argv
would be).
As an added bonus, when your module has an import-able main
function, you can write unit tests for it in the same way =)
Upvotes: 0
Reputation: 1091
After experimenting with this for quite some time, it turns out that this approach actually works:
python -c "import runpy; import pdb; pdb.runcall(runpy.run_module, 'somepackage.somemodule', run_name='__main__')"
For some reason, the use of pdb.runcall
over pdb.run
is important.
Upvotes: 10