287

This is a question I have wondered about for quite some time, yet I have never found a suitable solution. If I run a script and I come across, let's say an IndexError, python prints the line, location and quick description of the error and exits. Is it possible to automatically start pdb when an error is encountered? I am not against having an extra import statement at the top of the file, nor a few extra lines of code.

jeremy
  • 4,421
  • 4
  • 23
  • 27

15 Answers15

615
python -m pdb -c continue myscript.py
# or
python -m pdb -c continue -m myscript

If you don't provide the -c continue flag then you'll need to enter 'c' (for Continue) when execution begins. Then it will run to the error point and give you control there. As mentioned by eqzx, this flag is a new addition in python 3.2 so entering 'c' is required for earlier Python versions (see https://docs.python.org/3/library/pdb.html).

Intrastellar Explorer
  • 3,005
  • 9
  • 52
  • 119
Catherine Devlin
  • 7,383
  • 2
  • 25
  • 17
  • 1
    Wow, thanks! If now this initial call of PDB could also be avoided things would be perfect. Any ideas? – flonk Apr 04 '13 at 09:24
  • 10
    Thanks for mentioning the "_enter 'c'_" - I typically used to enter 'r' (for "run"), being used to it from `gdb`; and when you enter 'r' in `pdb`, program does indeed run, but does NOT stop (nor generate backtrace) on error; had me puzzled until I read this. Cheers! – sdaau Aug 02 '13 at 20:58
  • 4
    I tried your method. But it doesn't seem to offer any real control like inspecting variables at the the time of error. The only option it seems to offer is to restart execution. – Flame of udun Dec 07 '14 at 02:44
  • 3
    Vineet, it will start you with the debugger on, so enter "cont" and it will run until the error is encountered. From there you can inspect variables, etc. like in any other pdb session. – Catherine Devlin Dec 17 '14 at 16:41
  • 67
    Please OP, accept this as an answer. This is the most useful and I have wasted 5 minutes reading the other ones until I hit this one... this should be first ! – jhegedus Sep 17 '15 at 19:46
  • 5
    This also works the same with `ipdb`; and of course arguments can be added after the script! – tutuDajuju Dec 14 '15 at 10:03
  • This is the answer I was looking for. The accepted answer requires adding code just to launch a debug session, which is highly inconvenient. – Moises Silva Jan 27 '16 at 17:09
  • 1
    OP, please don't accept this as an answer. This does not work for KeyError, for example. It starts a debugger session in the beginning of the script again, and not in the line of the error. The selected answer (type, value, tb = sys.exc_info() + traceback.print_exc() + pdb.post_mortem(tb) ) is the right one. – corporateAbaper Aug 04 '16 at 10:44
  • @tutuDajuju, how do you get this to work with `ipdb`? I tried simply changing `pdb` in the above command to `ipdb`, but it gave me an error saying that "-c does not exist." – Vivek Subramanian Mar 07 '17 at 18:23
  • @vivek seems this was [fixed recently](https://github.com/gotcha/ipdb/commit/2381b128f5c62a0426e16465bfb9f86295dd7b94) but was not yet released to pypi. – tutuDajuju Mar 07 '17 at 19:25
  • 21
    This doesn't work with Python 2.7. https://docs.python.org/3/library/pdb.html : "New in version 3.2: pdb.py now accepts a -c option that executes commands" – eqzx Apr 10 '17 at 18:29
  • 1
    @eqzx The rest of the answer remains valid. We only have to hit 'c' after execution ... the rest is fine! – AruniRC Dec 19 '17 at 15:27
  • 1
    and do `pip install pdbpp` before using above, for nicer the UI. – Sławomir Lenart Feb 12 '19 at 16:04
  • 1
    @eqzx one solution to not having the `-c` flag in Python 2.7 is `(echo c && cat) | python -pdb script.py` – Ben Usman Mar 21 '19 at 19:26
  • 1
    https://stackoverflow.com/a/242531/237059 more accurately responds to the exact OP request -- they're not asking "how can I wrap the whole app in the debugger all the time?" – Stabledog Nov 21 '19 at 18:30
  • 1
    very handy with: alias pd='python3 -m pdb -c continue' – Yordan Grigorov Dec 15 '20 at 21:22
  • the need for `-c continue` seems rather strange or redudant...why is that formally needed? why does pdb not just do it out of the box? Also why do you need the `-m` flag? – Charlie Parker Jun 29 '22 at 18:29
  • btw mentioning `ipython --pdb myscript.py` and the other options you mentioned could be nice – Charlie Parker Jun 29 '22 at 18:29
156

You can use traceback.print_exc to print the exceptions traceback. Then use sys.exc_info to extract the traceback and finally call pdb.post_mortem with that traceback

import pdb, traceback, sys

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        extype, value, tb = sys.exc_info()
        traceback.print_exc()
        pdb.post_mortem(tb)

If you want to start an interactive command line with code.interact using the locals of the frame where the exception originated you can do

import traceback, sys, code

def bombs():
    a = []
    print a[0]

if __name__ == '__main__':
    try:
        bombs()
    except:
        type, value, tb = sys.exc_info()
        traceback.print_exc()
        last_frame = lambda tb=tb: last_frame(tb.tb_next) if tb.tb_next else tb
        frame = last_frame().tb_frame
        ns = dict(frame.f_globals)
        ns.update(frame.f_locals)
        code.interact(local=ns)
Florian Bösch
  • 27,420
  • 11
  • 48
  • 53
  • the first solution is further discussed at the [python cookbook](http://code.activestate.com/recipes/65287-automatically-start-the-debugger-on-an-exception/) – dirkjot Jun 27 '12 at 20:12
  • 4
    why would anyone prefer `code` over `pdb` since the latter seems to expand on the former? – K3---rnc Aug 25 '14 at 21:49
  • I do have the same question ? Why would you prefer `code` ? – ARH Feb 19 '15 at 23:06
  • 4
    *Then use `sys.exc_info` to extract the traceback and finally call `pdb.post_mortem` with that traceback*. You don't need to pass traceback object to `pdb.post_mortem`. From [docs](https://docs.python.org/2/library/pdb.html): *If no traceback is given, it uses the one of the exception that is currently being handled (an exception must be being handled if the default is to be used).* – Piotr Dobrogost Apr 21 '15 at 08:13
  • 2
    @PiotrDobrogost Good point. I think it's more helpful to know that you can pass a tb object in, though, as it better demonstrates the API. Good to know both options exist. – davidA Dec 18 '16 at 23:35
  • I'm putting these snippets in context managers in my util.py -- https://pastebin.com/7rC8EsuC – loxaxs May 16 '18 at 09:17
  • Is this as good as Common Lisp's restart system? Say you wrote "5" + num when you meant to write "5" + str(num), is it possible after doing post_mortem to fix your bug then continue off as if nothing happened? – aoeu256 Sep 16 '19 at 05:41
  • 1
    Regarding "why would you prefer code over pdb on the command line?" -- there are contexts where the interpreter is launched from within a process that doesn't allow you to launch the debugger as a wrapper... e.g. when the interpreter is just part of a larger application or some framework is managing a complex environment surrounding the interpreter. In such cases, doing a stub launcher like this makes sense. – Stabledog Oct 30 '19 at 18:45
  • @aoeu256 PyTrace https://pytrace.com/ time travelling debugger can step back and alter things like the CL frame restart system – ocodo May 17 '23 at 15:59
75

Use the following module:

import sys

def info(type, value, tb):
    if hasattr(sys, 'ps1') or not sys.stderr.isatty():
    # we are in interactive mode or we don't have a tty-like
    # device, so we call the default hook
        sys.__excepthook__(type, value, tb)
    else:
        import traceback, pdb
        # we are NOT in interactive mode, print the exception...
        traceback.print_exception(type, value, tb)
        print
        # ...then start the debugger in post-mortem mode.
        # pdb.pm() # deprecated
        pdb.post_mortem(tb) # more "modern"

sys.excepthook = info

Name it debug (or whatever you like) and put it somewhere in your python path.

Now, at the start of your script, just add an import debug.

compie
  • 10,135
  • 15
  • 54
  • 78
tzot
  • 92,761
  • 29
  • 141
  • 204
  • 4
    This should be the accepted answer -- it doesn't require any modification of existing code or wrapping everything in a `try-catch` which is just ugly IMO. – cyphar Sep 07 '15 at 15:01
  • 1
    This looks great, but note that some frameworks (e.g. flask) already set `sys.excepthook`. They often have their own even better approaches like werkzeug, but see also [python - Flask and sys\.excepthook - Stack Overflow](https://stackoverflow.com/questions/21627429/flask-and-sys-excepthook) – nealmcb Nov 06 '20 at 02:11
62

Ipython has a command for toggling this behavior: %pdb. It does exactly what you described, maybe even a bit more (giving you more informative backtraces with syntax highlighting and code completion). It's definitely worth a try!

Latanius
  • 2,553
  • 2
  • 23
  • 21
  • 6
    And that's the only reasonable answer to this. – Michael Mar 18 '16 at 08:47
  • 7
    Documented at http://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-pdb – matthiash Mar 03 '17 at 14:38
  • 8
    Note that -- as also noted in the docs @matthiash linked -- `%debug` allows one to open the debugger _after_ encountering an error. I often prefer this over `%pdb`. (The trade-off is merely typing `q` every time you don't want to debug an error vs. typing `%debug` every time you _do_ want to debug an error.) – Braham Snyder Oct 29 '17 at 21:12
  • 3
    Note also that setting `c.InteractiveShell.pdb = True` in one's `ipython_config.py` turns on `%pdb` automatically for every session. – Braham Snyder Oct 29 '17 at 21:17
35

This isn't the debugger, but probably just as useful(?)

I know I heard Guido mention this in a speech somewhere.

I just checked python -?, and if you use the -i command you can interact where your script stopped.

So given this script:

testlist = [1,2,3,4,5, 0]

prev_i = None
for i in testlist:
    if not prev_i:
        prev_i = i
    else:
        result = prev_i/i

You can get this output!

PS D:\> python -i debugtest.py
Traceback (most recent call last):
  File "debugtest.py", line 10, in <module>
    result = prev_i/i
ZeroDivisionError: integer division or modulo by zero
>>>
>>>
>>> prev_i
1
>>> i
0
>>>

To be honest I haven't used this, but I should be, seems very useful.

monkut
  • 42,176
  • 24
  • 124
  • 155
30

IPython makes this simple on the command line:

python myscript.py arg1 arg2

can be rewritten to

ipython --pdb myscript.py -- arg1 arg2

Or, similarly, if calling a module:

python -m mymodule arg1 arg2

can be rewritten to

ipython --pdb -m mymodule -- arg1 arg2

Note the -- to stop IPython from reading the script's arguments as its own.

This also has the advantage of invoking the enhanced IPython debugger (ipdb) instead of pdb.

wodow
  • 3,871
  • 5
  • 33
  • 45
6

If you are using the IPython environment, you can just use the %debug and the shell will take you back to the offending line with the ipdb environment for inspections etc. Another option as pointed above is to use the iPython magic %pdb which effectively does the same.

Jehandad
  • 414
  • 4
  • 13
  • Note that if the error occurred in a module function, you can navigate through the frames with `up` and `down` commands to go back to the line of your code that generated the error. – Jean Paul Jan 23 '19 at 10:23
4

To have it run without having to type c at the beginning use:

python -m pdb -c c <script name>

Pdb has its own command line arguments: -c c will execute c(ontinue) command at start of execution and the program will run uninterrupted until the error.

2

If you are running a module:

python -m mymodule

And now you want to enter pdb when an exception occurs, do this:

PYTHONPATH="." python -m pdb -c c mymodule/__main__.py

(or extend your PYTHONPATH). The PYTHONPATH is needed so that the module is found in the path, since you are running the pdb module now.

blueFast
  • 41,341
  • 63
  • 198
  • 344
2

python -m pdb script.py in python2.7 press continue to start and it will run to the error and break there for debug.

Hannah
  • 39
  • 1
2

ipdb has a nice context manager to achieve this behavior which makes the intent semantically clearer:

from ipdb import launch_ipdb_on_exception

with launch_ipdb_on_exception():
    ...
dlukes
  • 1,313
  • 16
  • 27
1

You can put this line in your code:

import pdb ; pdb.set_trace()

More info: Start the python debugger at any line

Amandasaurus
  • 58,203
  • 71
  • 188
  • 248
  • 11
    This stops the code and starts a debugger in the line where you put this command, not on the line where an exception occurred – blueFast Feb 18 '16 at 17:53
0

Since 3.7, you can use the keyword breakpoint directly in your code (without any import), just like this:

try:
    ...  # The line that raises an error
except:
    breakpoint()
Jonathan Dauwe
  • 317
  • 2
  • 6
0

If you happen to be using pudb (python debugger with a nice interface), you can enter debugger when an exception occurs with:

python -m pudb -c code_that_crashes.py

pudb describes the -c option as:

-c, --continue  Let the script run until an exception occurs or a breakpoint is hit
Harsh Verma
  • 529
  • 6
  • 10
-1

Put a breakpoint inside the constructor of topmost exception class in the hierarchy, and most of the times you will see where the error was raised.

Putting a breakpoint means whatever you want it to mean : you can use an IDE, or pdb.set_trace, or whatever

vlad-ardelean
  • 7,480
  • 15
  • 80
  • 124