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.

有帮助吗?

解决方案 2

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.

其他提示

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top