32

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.

5 Answers 5

30

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
Sign up to request clarification or add additional context in comments.

1 Comment

python -m pdb -m path.to.module works perfectly for me in python 3.9.5
12

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.

1 Comment

pdb.run would expect a string, not a callable. python -c "import runpy; import pdb; pdb.run(\"runpy.run_module( 'somepackage.somemodule', run_name='__main__')\")" works just as well, but it's more cumbersome
3

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

Comments

0

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

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.