skip to content

In pdb, how do the step, next, until and return commands differ when stepping?

level: middleimportance: must knowfreq 50%

answer

  1. Two of them differ only at calls
  2. One enters the callee, one skips it
  3. next stays in the current frame
  4. One runs past a higher line number
  5. return stops just before the frame exits

basics

~10 s

In pdb, step enters a called function, next runs the call and stays in the current frame, return finishes the current function, and until runs forward past the current line number.

solid answer

~50 s

All four advance execution; they differ in how far. `step` (`s`) stops at the first opportunity — inside a function called on this line, otherwise on the next line. `next` (`n`) executes the whole line, calls included, and stops at the next line of the **same** frame, so it steps *over* a call. `return` (`r`) runs the rest of the current function and stops just before it returns to its caller. `until` (`u`) with no argument continues until a line with a number greater than the current one is reached or the frame returns — the practical way to finish a loop body you are standing at the bottom of; with an argument it runs until that line number. Any breakpoint hit inside a skipped call still stops execution, so `next` is not a guarantee of not stopping.

code

python · 11 lines
python
def leg_cost(distance_km):
    rate = 0.42
    return distance_km * rate

def total_cost(legs):
    total = 0.0
    for distance_km in legs:
        total += leg_cost(distance_km)
    return total

print(total_cost([12.0, 30.5, 7.25]))

go deeper

for a junior

Memorise the two you will use constantly: n runs the current line and stays in this function, s goes inside the function the line calls. Add c to resume and l to see where you are, and you can drive a session.

for a middle

Explain that s and n differ only at call sites, that r finishes the current frame, and that until is line-number based rather than condition based. Be ready to say that breakpoints still fire inside a call you stepped over.

for a senior

Demonstrate command choice under pressure: r to unwind a wrong descent, until to escape a hot loop instead of stepping thousands of iterations, and breakpoints in a generator or callback rather than trying to step into it from the consumer.

for a principal

Frame stepping as one tool among several. Argue when a debugger session beats structured logging or a targeted test — interactive stepping does not scale to concurrent or production systems, and the ability to reproduce a failure in a single-threaded harness is what makes stepping viable at all.

## One axis: how much execution before the next prompt The four commands are best learned as increasing spans of execution, all bounded by breakpoints. ### step (`s`) — the finest grain `step` executes the current line and stops at the *first* place it can. If the line calls a Python function, that is the first line inside the callee, and you are now in a new frame. If it calls nothing, it behaves like `next`. If the current line returns, you land back in the caller. `step` is how you get *into* code; it is also how you get lost, because it descends into every helper, comprehension body and property getter on the way. One limit worth knowing: `pdb` traces Python frames. It cannot step into a function implemented in C, so stepping at a line that calls into an extension or a builtin simply runs that call to completion. ### next (`n`) — the everyday command `next` executes the current line *including any calls it makes* and stops at the next line of the **same** frame, or just before that frame returns. This is stepping *over*. It is the command you use for nine lines out of ten, because you usually trust the callee and want to see what this function does. The difference between `s` and `n` therefore only exists on lines that call something. On a plain assignment they are identical. ### return (`r`) — finish this frame `return` runs the remainder of the current function and stops just as it is about to return to its caller. Use it when `step` took you somewhere you did not want to be: `r` gets you back out to the caller's line without hand-stepping through the rest of the callee. It is also how you observe the value a function is about to hand back before the caller does anything with it. ### until (`u`) — get past the loop `until` with no argument continues execution until a line **greater than the current line number** is reached in the current frame, or until the frame returns. Standing on the last line of a loop body, plain `n` sends you back to the top of the loop for iteration two; `until` instead runs the loop to completion and stops on the first line after it. That is its whole reason to exist, and it is the answer to "how do I skip 6,800 iterations without setting a breakpoint". `until lineno` is the argument form: run until a line at or after `lineno` is reached in the current frame, or the frame returns. Note the frame-return escape hatch in both forms — `until` never runs away from the function you are in. ### continue (`c`) — the outer bound `continue` resumes normal execution until the next breakpoint, or until the program ends. Everything above is a bounded version of this. ## The rule that governs all of them **Breakpoints win.** If a `break` is set inside a function you step *over* with `n`, execution still stops there. `n` does not disable tracing; it only says "do not give me a prompt until I am back in this frame". Candidates who describe `next` as "skips the function" are wrong in a way that matters when debugging recursion or callbacks. ## Two consequences people meet in practice **Recursion.** `n` at a recursive call runs the entire subtree of recursive calls. `s` enters the next level. `r` unwinds exactly one level. Knowing which one you want is most of what makes debugging recursion tolerable. **Generators and callbacks.** Stepping over a line that consumes a generator runs however much of the generator body that consumption requires, in a frame you never see. If the interesting logic lives in the generator, set a breakpoint in it rather than trying to step into it from the consumer. ## Getting oriented after a step Stepping is only half the job; knowing where you are is the other half. `where` (also `w`, `bt`) prints the stack with the current frame marked, `list` (`l`) shows source around the current line, and `up`/`down` move the *inspection* frame without executing anything — an important distinction, because after `up` a subsequent `n` still resumes execution at the real execution point. Quitting is `q`, which raises `bdb.BdbQuit` and tears the program down; `c` is the way to let it finish normally.

  • You typed next over a call, but the debugger stopped inside that call anyway. Why?
    A breakpoint inside the callee was hit. next does not switch tracing off; it only suppresses prompts until control is back in the current frame, and any active breakpoint still fires. That is usually what you want, but it surprises people who think of next as skipping the function. Delete or disable the breakpoint, or use ignore counts, if you really need to run through untouched.
  • How do you get out of a function you stepped into by mistake without stepping through the rest of it?
    Use return (r) to run the remaining body and stop just before the frame returns, then next to land back on the caller's next line. up only changes which frame you are inspecting, it does not resume execution, so it will not get you out — it just lets you read the caller's locals while execution is still suspended deep in the callee.
  • Why is until useful at the bottom of a loop body when next is not?
    next goes to the next line executed, which at the bottom of a loop is the top of the loop for the next iteration, so you re-walk the body every time. until with no argument continues until a line number greater than the current one is reached in this frame, which means the first line after the loop — you exit the loop in one command however many iterations remain.

saying these in an interview costs you the question

  • Says next skips the function so breakpoints inside cannot fire
  • Thinks step and next differ on every line, not only on calls
  • Believes until means until a condition becomes true
  • Confuses return with continue and expects the program to finish
  • Claims up resumes execution in the caller frame
  • Expects step to enter a C-implemented function

context