Addaly is in open beta. Things will change, and AI answers can be wrong — check anything that matters.

Python, From Zero, For AI

From your first line of code to your first API call.

Lesson 35 of 898 min

The debugger: stopping the program and looking around

One word, and the program pauses

Put this line anywhere:

python
breakpoint()

Run the script normally. Execution stops there and you get a prompt:

> /home/asha/billing/report.py(42)build()
-> total = sum(row["amount"] for row in rows)
(Pdb)

You are now inside the running program, at that line, with every variable alive. This is the standard library's debugger, pdb, and breakpoint() has been the way to enter it since Python 3.7. You do not install anything.

Eight commands do almost everything

  • l (list) — show the code around where you are. ll shows the whole function.
  • p expr — print an expression. pp pretty-prints a big structure.
  • n (next) — run the current line and stop at the next one in this function.
  • s (step) — same, but step into a function call.
  • c (continue) — run on until the next breakpoint or the end.
  • w (where) — print the call stack: how you got here.
  • u / d — move up and down that stack, to inspect a caller's variables.
  • q (quit) — stop the program.

At the (Pdb) prompt you can also just type Python. len(rows), rows[0].keys(), type(total) all work, and so does calling a function to see what it returns with the actual data. That is the advantage over print statements: you ask new questions without rerunning the program. On a job that takes four minutes to reach the failure, that difference is the afternoon.

One thing to know: p is a command, and if a variable is called n, s, c or l, typing its name runs the command instead. p n prints the variable.

After the crash, not before

The most useful mode is post-mortem. When a script dies, run it again like this:

bash
python3 -m pdb -c continue script.py

It runs at full speed, and on the exception it drops you into the debugger at the exact frame where it happened, with everything still in place. u walks up to the caller. You get the values that produced the crash, which a traceback alone does not give you.

In a notebook, %debug in the next cell does the same thing for the cell that just failed.

Breaking only when it matters

A breakpoint inside a loop over 100,000 rows is useless — you would press c all afternoon. Two ways round it.

Guard it in code:

python
if row["id"] == "ADD-4471":
    breakpoint()

Or set a conditional breakpoint from the prompt:

(Pdb) b report.py:57, row["amount"] < 0

b sets a breakpoint at a file and line, and anything after the comma is a condition. This is how you catch the one bad row in a million.

The editor version

VS Code, PyCharm and Thonny all wrap the same machinery in a window: click in the margin to set a breakpoint, press the debug button, and see variables in a panel rather than typing p. Everything above still applies; the commands are buttons. Thonny is the gentlest, and it draws the call stack as boxes, which makes the earlier lesson on scope much more concrete.

Use whichever you have. The skill is knowing what to ask, not which key to press.

When print is still better

The debugger is not always the right tool, and it is honest to say so.

  • A bug that only appears across 10,000 iterations is better found by logging a value each time and looking at the pattern. You cannot see a trend one breakpoint at a time.
  • Anything involving timing or concurrency changes behaviour when you stop it. Pausing one thread while others run can make the bug disappear, or create a new one.
  • Code running on a server you cannot attach to leaves logging as the only option, which is why the previous lesson matters.

A reasonable habit: logging for what happened across a whole run, the debugger for one specific moment you need to look at closely.

Try this now

Take a script that fails, and instead of adding prints, run python3 -m pdb -c continue yourscript.py. When it drops you at the failure, type w, then u, then print the variables in the calling frame. Two minutes, and it will change how you debug.

The one thing to keep

`breakpoint()` and `python -m pdb -c continue` put you inside the running program at the moment of failure, where you can ask new questions of the live state instead of rerunning with more prints.

Before you move on

A pipeline crashes after four minutes on one row out of 800,000. Which approach gets to the cause fastest?

Pick the one you would defend. Nobody sees your answer.

No ads. No data sale. No public scores on people. Ever.

© 2026 Addaly