How to debug Python programs

Reproduce and reduce

First reproduce the problem with the same input. Read the complete traceback, then shrink the program or input until the failure remains in the smallest useful example.

def average(values):
    return sum(values) / len(values)

# Small failing case reveals division by zero:
print(average([]))

Inspect values deliberately

Temporary prints can reveal control flow and types. The !r conversion shows a representation that makes spaces and escape characters visible.

raw = " 42\n"
print(f"{raw=}, {type(raw)=}")
clean = raw.strip()
print(f"{clean=}")
print(int(clean))

State assumptions with assert

Assertions document internal invariants and stop near the real cause during development. Do not use them to validate untrusted user input because optimized runs can remove them.

def first_item(items):
    assert items, "items must not be empty"
    return items[0]

print(first_item(["Python", "HTML"]))

Pause with the debugger

breakpoint() opens Python's debugger at that line. Useful commands include p name, n for next line, s to step into, c to continue, and q to quit.

def total_with_tax(price, rate):
    subtotal = price
    breakpoint()
    return subtotal * (1 + rate)

print(total_with_tax(20, 0.1))