What does `python -m pdb script.py` do when the script dies with an uncaught exception?
answer
- The debugger outlives the crash
- You get a prompt, not a traceback
- It lands where the raise happened
- Frames are captured, not running
- continue restarts instead of resuming
basics
~20 sInstead of printing a traceback and exiting, pdb catches the unhandled exception and drops you into post-mortem debugging at the frame that raised, with that frame's local variables still readable. Execution cannot be resumed from the raise point.
solid answer
~40 s`python -m pdb script.py` runs the script under the debugger, pausing before its first statement; add `-c continue` to skip that pause and only stop on trouble. If the script raises an exception nobody handles, pdb prints the traceback, says it is entering post mortem debugging, and gives you a prompt positioned in the **innermost frame of the traceback** - the frame that actually raised. There you can print names, and walk the captured stack with `up` and `down` to see the caller's state. The frames are dead: you are inspecting a snapshot, not a live program, so nothing resumes from the raise. Typing `continue` or `step` restarts the whole script under the same debugger session, keeping any breakpoints you set; `q` quits.
code
console · 1 linepython -m pdb -c continue bidder.pygo deeper
Recall the one-liner: python -m pdb script.py, and that a crash leaves you at a prompt inside the function that raised instead of just printing a traceback. Be able to say that you can print variables there.
Explain that the frames are captured by the traceback and already unwound, which is why nothing resumes and why continue restarts the program. Know -c continue for running straight to the failure and -m for debugging a module.
Show judgement about when this applies at all: it needs a terminal and a start-it-yourself script. Be ready to say what you would do instead for a scheduled job or an already-running process, and what post-mortem state cannot tell you about how the bug developed.
Own the policy question: which failures deserve an interactive reproduction at all versus a captured artefact, and what your services must record at raise time so that a crash is diagnosable once without anyone needing to reproduce it.
### What the launcher actually does `python -m pdb script.py` executes `script.py` under the control of `pdb`, the standard library's debugger. Arguments after the script name are passed to the script, so `python -m pdb bidder.py --dry-run` gives the script its own `--dry-run`. Since 3.7 you can debug a module instead of a file with `python -m pdb -m package.module`, and `-c CMD` runs a pdb command before execution starts, which is how `-c continue` turns the debugger from *interactive from line one* into a *crash trap*. By default the launcher stops at the first statement of the script and gives you a prompt. That is the pre-failure use of the debugger. The behaviour that makes the launcher interesting for debugging a crash is what happens at the other end. ### Post-mortem entry Normally an exception that no `except` clause handles unwinds every frame, the interpreter prints the traceback to standard error, and the process exits with a non-zero status. The frames are gone by the time you read the traceback, so the only state you have is what the traceback text happened to print. Under `python -m pdb`, the debugger is the outermost caller, so it sees the exception before the process can die. It prints the traceback, prints `Uncaught exception. Entering post mortem debugging`, and opens a prompt. Crucially the prompt is *not* at the top of the program: pdb walks to the innermost frame recorded in the exception's traceback - the frame where the `raise` (or the failing operation) happened - and puts you there. The line marker shows the offending source line. That means the local variables of the raising function are still reachable. If a list index blew up, you can print the list and the index; if a lookup failed, you can print the key and the container. This is the whole value proposition of post-mortem work: you debug the failure you already have rather than adding a breakpoint and re-running, hoping the failure reproduces. ```console $ python -m pdb -c continue bidder.py IndexError: list index out of range Uncaught exception. Entering post mortem debugging Running 'cont' or 'step' will restart the program > /srv/bidder.py(2)settle() -> return order[2] (Pdb) p order ['impression', 'click'] ``` ### The frames are dead The single most important property of post-mortem mode, and the one candidates get wrong, is that the stack you are standing in has already unwound. The traceback holds references to the frame objects, which is why their names are still readable, but the interpreter is no longer executing them. You therefore cannot step forward from the raise, cannot 'fix' a variable and continue, and cannot see what the next line would have done. `up` and `down` move your view along the captured chain of frames; that is navigation, not execution. Because there is nothing to resume, pdb repurposes the resume commands: typing `continue` or `step` in post-mortem mode **restarts the program from the beginning** under the same debugger session, which is what the banner is warning you about. State such as breakpoints survives the restart, so the normal workflow is: crash, inspect the raising frame, set a breakpoint slightly earlier, then restart and watch the failure develop. `q` leaves the debugger. ### Reading the traceback pdb prints Since 3.11 (PEP 657) tracebacks carry column information, so the traceback printed just before the post-mortem prompt underlines the exact subexpression that failed - useful when a line contains several subscripts or calls and the exception type alone does not say which one blew up. ### What the snapshot cannot tell you Inspection is limited to what the frames actually hold at the instant of the raise. A local that had not been assigned yet simply is not there; a name that was rebound shows only its last value; an iterator that was partially consumed shows its exhausted state, not the items it yielded. Comprehensions and generator expressions run in frames of their own, so a failure inside one lands you in a frame whose only names are the loop variable and the iterable. Post-mortem answers 'what was true when it broke', never 'how did it get that way' - which is why the standard workflow is to inspect the wreck, form a hypothesis about an earlier moment, set a breakpoint there, and let `continue` restart the program to test it. ### When this is the wrong tool The launcher requires a terminal and a script you can start yourself. It does not help with a process that is already running (that is what an attach mechanism is for), and it does not help with a batch job on a scheduler where nobody is watching the tty - a debugger prompt there simply hangs until something kills it. For those, the same post-mortem idea is applied from inside the program instead: catch the exception, and either hand its traceback to the debugger explicitly or serialize the frames' state for later reading.
- Why does typing `continue` at a post-mortem prompt restart the script rather than resume it?Because there is nothing to resume. The exception already unwound every frame; the debugger is only holding references to frame objects captured by the traceback, not a suspended program counter. pdb therefore reuses `continue` and `step` to re-run the program from the top under the same session, preserving breakpoints, which is usually what you want next: inspect the wreck, set a breakpoint just before it, run again.
- What do you lose by debugging under `-c continue` rather than adding a breakpoint up front?You lose the history: you see the state at the moment of the raise, not how it got there. Any variable that was rebound between the bad decision and the failure shows only its final value, and anything consumed on the way - an exhausted iterator, a closed file, a popped item - is simply gone. Post-mortem tells you what was true when it broke; stepping tells you why it became true.
- Does `python -m pdb` change the process exit status when the script crashes?Yes. The debugger, not the interpreter's default handler, is in charge of the unhandled exception, so the script's own crash no longer decides the exit. That is fine for interactive work but it is one more reason not to wrap production entry points in the debugger: anything that keys off exit status - a supervisor, a scheduler, a CI step - stops seeing the failure.
It is the difference between reading an accident report and standing in the wreck: the traceback is the report, post-mortem mode walks you around the vehicle while everything is still where it stopped.
saying these in an interview costs you the question
- Thinks post-mortem mode can resume execution after the raise
- Expects the prompt at the top-level frame, not the raising one
- Believes `continue` skips past the exception
- Confuses it with attaching to an already-running process
- Assumes the traceback text alone shows local variable values
- Suggests wrapping a headless production job in `python -m pdb`