[{"data":1,"prerenderedAt":1528},["ShallowReactive",2],{"page-\u002Fsystematic-debugging-performance-profiling\u002Finteractive-debugging-with-pdb-and-ipdb\u002F":3},{"id":4,"title":5,"body":6,"description":1492,"extension":1493,"meta":1494,"navigation":420,"path":1524,"seo":1525,"stem":1526,"__hash__":1527},"content\u002Fsystematic-debugging-performance-profiling\u002Finteractive-debugging-with-pdb-and-ipdb\u002Findex.md","Interactive Debugging with pdb and ipdb",{"type":7,"value":8,"toc":1474},"minimark",[9,33,38,103,107,163,179,350,354,359,375,439,466,472,519,523,552,608,624,628,641,695,699,722,769,836,840,843,904,934,938,954,989,993,999,1041,1083,1095,1099,1119,1148,1152,1155,1203,1207,1346,1350,1368,1392,1406,1427,1431,1463,1470],[10,11,12,13,17,18,21,22,25,26,28,29,32],"p",{},"A test fails with an opaque ",[14,15,16],"code",{},"AssertionError",", or a service hangs in production and the stack trace points at a line that \"cannot\" be wrong. Print-statement archaeology answers one question per round-trip and pollutes the codebase; an interactive debugger answers every question at the exact moment of failure. ",[14,19,20],{},"pdb"," ships with CPython, attaches to any frame, and — since Python 3.7 — is reachable from a single ",[14,23,24],{},"breakpoint()"," call. This guide covers driving ",[14,27,20],{}," and ",[14,30,31],{},"ipdb"," fluently: the full command vocabulary, frame navigation, and the integration points that let you drop straight into the debugger from a failing pytest run.",[34,35,37],"h2",{"id":36},"prerequisites","Prerequisites",[39,40,41,60,80,89],"ul",{},[42,43,44,48,49,51,52,55,56,59],"li",{},[45,46,47],"strong",{},"Python 3.7+"," for the built-in ",[14,50,24],{}," function and the ",[14,53,54],{},"PYTHONBREAKPOINT"," hook (older versions require an explicit ",[14,57,58],{},"import pdb; pdb.set_trace()",").",[42,61,62,65,66,68,69,71,72,75,76,79],{},[45,63,64],{},"Python 3.13+"," if you want ",[14,67,24],{}," to honour ",[14,70,20],{},"'s new ",[14,73,74],{},"commands"," reuse and the improved ",[14,77,78],{},"display"," semantics; everything else here works from 3.7.",[42,81,82,84,85,88],{},[45,83,31],{}," (",[14,86,87],{},"pip install ipdb",") for the IPython-backed prompt. It tracks the installed IPython version; pin both in CI if you depend on the rendering.",[42,90,91,94,95,98,99,102],{},[45,92,93],{},"pytest 5.4+"," for ",[14,96,97],{},"--trace"," (stop at the start of each test) alongside the long-standing ",[14,100,101],{},"--pdb"," (post-mortem on failure).",[34,104,106],{"id":105},"core-concept","Core concept",[10,108,109,111,112,115,116,119,120,122,123,125,126,125,129,125,132,135,136,140,141,125,144,147,148,125,151,125,154,125,157,125,160,59],{},[14,110,20],{}," is a thin interactive layer over ",[14,113,114],{},"sys.settrace",". When you call ",[14,117,118],{},"pdb.set_trace()"," (or ",[14,121,24],{},"), the debugger installs a trace function on the current frame and hands control to a command loop bound to that frame. Every command either inspects the frame (",[14,124,10],{},", ",[14,127,128],{},"pp",[14,130,131],{},"list",[14,133,134],{},"where","), moves the ",[137,138,139],"em",{},"current frame pointer"," up or down the call stack without resuming execution (",[14,142,143],{},"up",[14,145,146],{},"down","), or resumes the interpreter under a stopping condition (",[14,149,150],{},"next",[14,152,153],{},"step",[14,155,156],{},"until",[14,158,159],{},"continue",[14,161,162],{},"return",[10,164,165,166,169,170,173,174,28,176,178],{},"The critical mental model is the distinction between ",[45,167,168],{},"the execution point"," (where the interpreter is actually paused) and ",[45,171,172],{},"the current frame"," (the stack level the commands operate on). ",[14,175,143],{},[14,177,146],{}," move only the latter, letting you read a caller's locals without losing your place. The flow below traces a single session from breakpoint to resolution.",[180,181,184,346],"figure",{"className":182},[183],"diagram",[185,186,193,194,193,198,193,202,193,212,193,223,193,228,193,233,193,237,193,240,193,243,193,247,193,251,193,254,193,260,193,264,193,268,193,271,193,278,193,284,193,287,193,291,193,295,193,299,193,303,193,307,193,311,193,318,193,322,193,326,193,330,193,332,193,336,193,343],"svg",{"viewBox":187,"role":188,"ariaLabelledBy":189,"xmlns":192},"0 0 820 380","img",[190,191],"pdbflow-title","pdbflow-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[195,196,197],"title",{"id":190},"pdb session flow and the frame stack",[199,200,201],"desc",{"id":191},"A breakpoint enters the command loop, which inspects the frame, navigates the call stack with up and down, and resumes with continue or step.",[203,204,211],"text",{"x":205,"y":206,"textAnchor":207,"fontSize":208,"fontWeight":209,"fill":210},"410","34","middle","19","700","#3d405b","A single pdb session",[213,214],"rect",{"x":215,"y":216,"width":217,"height":218,"rx":219,"fill":220,"stroke":221,"strokeWidth":222},"36","60","180","64","12","#fffdf8","#e07a5f","2",[203,224,24],{"x":225,"y":226,"textAnchor":207,"fontSize":227,"fontWeight":209,"fill":210},"126","88","14",[203,229,232],{"x":225,"y":230,"textAnchor":207,"fontSize":231,"fill":210},"108","11","installs trace hook",[213,234],{"x":235,"y":216,"width":217,"height":218,"rx":219,"fill":220,"stroke":236,"strokeWidth":222},"320","#81b29a",[203,238,239],{"x":205,"y":226,"textAnchor":207,"fontSize":227,"fontWeight":209,"fill":210},"command loop",[203,241,242],{"x":205,"y":230,"textAnchor":207,"fontSize":231,"fill":210},"(Pdb) prompt",[213,244],{"x":245,"y":216,"width":217,"height":218,"rx":219,"fill":220,"stroke":246,"strokeWidth":222},"604","#f2cc8f",[203,248,250],{"x":249,"y":226,"textAnchor":207,"fontSize":227,"fontWeight":209,"fill":210},"694","resume",[203,252,253],{"x":249,"y":230,"textAnchor":207,"fontSize":231,"fill":210},"n \u002F s \u002F c \u002F until",[255,256],"line",{"x1":257,"y1":258,"x2":259,"y2":258,"stroke":210,"strokeWidth":222},"216","92","318",[261,262],"polygon",{"points":263,"fill":210},"318,92 308,87 308,97",[255,265],{"x1":266,"y1":258,"x2":267,"y2":258,"stroke":210,"strokeWidth":222},"500","602",[261,269],{"points":270,"fill":210},"602,92 592,87 592,97",[213,272],{"x":215,"y":273,"width":274,"height":275,"rx":219,"fill":276,"stroke":210,"strokeWidth":277},"168","368","184","#f4f1de","1.5",[203,279,283],{"x":280,"y":281,"textAnchor":207,"fontSize":282,"fontWeight":209,"fill":210},"220","194","13","Inspect (frame stays put)",[203,285,286],{"x":280,"y":280,"textAnchor":207,"fontSize":219,"fill":210},"list \u002F ll  -  show source",[203,288,290],{"x":280,"y":289,"textAnchor":207,"fontSize":219,"fill":210},"244","where  -  full traceback",[203,292,294],{"x":280,"y":293,"textAnchor":207,"fontSize":219,"fill":210},"268","p \u002F pp  -  print value",[203,296,298],{"x":280,"y":297,"textAnchor":207,"fontSize":219,"fill":210},"292","display x  -  watch on stop",[203,300,302],{"x":280,"y":301,"textAnchor":207,"fontSize":231,"fill":221},"324","execution point unchanged",[213,304],{"x":305,"y":273,"width":306,"height":275,"rx":219,"fill":220,"stroke":210,"strokeWidth":277},"436","348",[203,308,310],{"x":309,"y":281,"textAnchor":207,"fontSize":282,"fontWeight":209,"fill":210},"610","Frame stack (up \u002F down)",[213,312],{"x":313,"y":314,"width":315,"height":316,"rx":317,"fill":276,"stroke":236,"strokeWidth":277},"470","212","280","30","6",[203,319,321],{"x":309,"y":320,"textAnchor":207,"fontSize":231,"fill":210},"232","handler()  \u003C- paused here",[213,323],{"x":313,"y":324,"width":315,"height":316,"rx":317,"fill":220,"stroke":210,"strokeWidth":325},"252","1.2",[203,327,329],{"x":309,"y":328,"textAnchor":207,"fontSize":231,"fill":210},"272","service.process()",[213,331],{"x":313,"y":297,"width":315,"height":316,"rx":317,"fill":220,"stroke":210,"strokeWidth":325},[203,333,335],{"x":309,"y":334,"textAnchor":207,"fontSize":231,"fill":210},"312","main()  -  up moves here",[255,337],{"x1":338,"y1":339,"x2":338,"y2":340,"stroke":210,"strokeWidth":325,"strokeDashArray":341},"350","124","166",[342,342],"4",[255,344],{"x1":205,"y1":339,"x2":309,"y2":340,"stroke":210,"strokeWidth":325,"strokeDashArray":345},[342,342],[347,348,349],"figcaption",{},"A pdb session: breakpoint() enters the command loop; inspection commands read the current frame while up and down walk the call stack; n, s, until, and c resume the interpreter.",[34,351,353],{"id":352},"step-by-step-implementation","Step-by-step implementation",[355,356,358],"h3",{"id":357},"_1-enter-the-debugger-with-breakpoint","1. Enter the debugger with breakpoint()",[10,360,361,362,364,365,368,369,371,372,374],{},"Since Python 3.7, ",[14,363,24],{}," is the idiomatic entry point. It calls ",[14,366,367],{},"sys.breakpointhook()",", which by default runs ",[14,370,118],{}," but can be redirected through the ",[14,373,54],{}," environment variable — so you never have to edit the call site to switch debuggers or disable it.",[376,377,382],"pre",{"className":378,"code":379,"language":380,"meta":381,"style":381},"language-python shiki shiki-themes github-light github-dark","# orders.py\ndef apply_discount(price, percent):\n    factor = 1 - percent \u002F 100\n    breakpoint()  # interpreter pauses here; the (Pdb) prompt appears\n    return round(price * factor, 2)\n\n\nif __name__ == \"__main__\":\n    print(apply_discount(100, 15))\n","python","",[14,383,384,391,397,403,409,415,422,427,433],{"__ignoreMap":381},[385,386,388],"span",{"class":255,"line":387},1,[385,389,390],{},"# orders.py\n",[385,392,394],{"class":255,"line":393},2,[385,395,396],{},"def apply_discount(price, percent):\n",[385,398,400],{"class":255,"line":399},3,[385,401,402],{},"    factor = 1 - percent \u002F 100\n",[385,404,406],{"class":255,"line":405},4,[385,407,408],{},"    breakpoint()  # interpreter pauses here; the (Pdb) prompt appears\n",[385,410,412],{"class":255,"line":411},5,[385,413,414],{},"    return round(price * factor, 2)\n",[385,416,418],{"class":255,"line":417},6,[385,419,421],{"emptyLinePlaceholder":420},true,"\n",[385,423,425],{"class":255,"line":424},7,[385,426,421],{"emptyLinePlaceholder":420},[385,428,430],{"class":255,"line":429},8,[385,431,432],{},"if __name__ == \"__main__\":\n",[385,434,436],{"class":255,"line":435},9,[385,437,438],{},"    print(apply_discount(100, 15))\n",[376,440,444],{"className":441,"code":442,"language":443,"meta":381,"style":381},"language-console shiki shiki-themes github-light github-dark","$ python orders.py\n> orders.py(4)apply_discount()\n-> return round(price * factor, 2)\n(Pdb)\n","console",[14,445,446,451,456,461],{"__ignoreMap":381},[385,447,448],{"class":255,"line":387},[385,449,450],{},"$ python orders.py\n",[385,452,453],{"class":255,"line":393},[385,454,455],{},"> orders.py(4)apply_discount()\n",[385,457,458],{"class":255,"line":399},[385,459,460],{},"-> return round(price * factor, 2)\n",[385,462,463],{"class":255,"line":405},[385,464,465],{},"(Pdb)\n",[10,467,468,469,471],{},"To route the same call through ",[14,470,31],{}," without changing source, or to disable every breakpoint in a CI run:",[376,473,477],{"className":474,"code":475,"language":476,"meta":381,"style":381},"language-bash shiki shiki-themes github-light github-dark","PYTHONBREAKPOINT=ipdb.set_trace python orders.py   # use ipdb's prompt\nPYTHONBREAKPOINT=0 python orders.py                # all breakpoint() calls become no-ops\n","bash",[14,478,479,503],{"__ignoreMap":381},[385,480,481,484,488,492,496,499],{"class":255,"line":387},[385,482,54],{"class":483},"sVt8B",[385,485,487],{"class":486},"szBVR","=",[385,489,491],{"class":490},"sZZnC","ipdb.set_trace",[385,493,495],{"class":494},"sScJk"," python",[385,497,498],{"class":490}," orders.py",[385,500,502],{"class":501},"sJ8bj","   # use ipdb's prompt\n",[385,504,505,507,509,512,514,516],{"class":255,"line":393},[385,506,54],{"class":483},[385,508,487],{"class":486},[385,510,511],{"class":490},"0",[385,513,495],{"class":494},[385,515,498],{"class":490},[385,517,518],{"class":501},"                # all breakpoint() calls become no-ops\n",[355,520,522],{"id":521},"_2-get-your-bearings-list-where-args","2. Get your bearings: list, where, args",[10,524,525,526,84,528,531,532,84,535,538,539,84,541,544,545,84,548,551],{},"The first three commands at any prompt orient you. ",[14,527,131],{},[14,529,530],{},"l",") prints source around the current line; ",[14,533,534],{},"longlist",[14,536,537],{},"ll",") prints the whole enclosing function. ",[14,540,134],{},[14,542,543],{},"w",") shows the full stack with the current frame marked. ",[14,546,547],{},"args",[14,549,550],{},"a",") prints the arguments of the current function.",[376,553,555],{"className":441,"code":554,"language":443,"meta":381,"style":381},"(Pdb) ll\n  1     def apply_discount(price, percent):\n  2         factor = 1 - percent \u002F 100\n  3         breakpoint()\n  4  ->     return round(price * factor, 2)\n(Pdb) a\nprice = 100\npercent = 15\n(Pdb) p factor\n0.85\n",[14,556,557,562,567,572,577,582,587,592,597,602],{"__ignoreMap":381},[385,558,559],{"class":255,"line":387},[385,560,561],{},"(Pdb) ll\n",[385,563,564],{"class":255,"line":393},[385,565,566],{},"  1     def apply_discount(price, percent):\n",[385,568,569],{"class":255,"line":399},[385,570,571],{},"  2         factor = 1 - percent \u002F 100\n",[385,573,574],{"class":255,"line":405},[385,575,576],{},"  3         breakpoint()\n",[385,578,579],{"class":255,"line":411},[385,580,581],{},"  4  ->     return round(price * factor, 2)\n",[385,583,584],{"class":255,"line":417},[385,585,586],{},"(Pdb) a\n",[385,588,589],{"class":255,"line":424},[385,590,591],{},"price = 100\n",[385,593,594],{"class":255,"line":429},[385,595,596],{},"percent = 15\n",[385,598,599],{"class":255,"line":435},[385,600,601],{},"(Pdb) p factor\n",[385,603,605],{"class":255,"line":604},10,[385,606,607],{},"0.85\n",[10,609,610,611,613,614,616,617,619,620,623],{},"Use ",[14,612,10],{}," for a single value and ",[14,615,128],{}," (pretty-print) for nested structures — ",[14,618,128],{}," routes through ",[14,621,622],{},"pprint"," and wraps large dicts and lists across lines instead of printing one unreadable row.",[355,625,627],{"id":626},"_3-inspect-state-with-p-pp-and-interact","3. Inspect state with p, pp, and interact",[10,629,630,631,633,634,636,637,640],{},"Any expression after ",[14,632,10],{},"\u002F",[14,635,128],{}," is evaluated in the current frame, so you can call methods and build throwaway computations. For deep exploration, ",[14,638,639],{},"interact"," drops you into a full Python REPL seeded with the frame's globals and locals — invaluable when single expressions are not enough.",[376,642,644],{"className":441,"code":643,"language":443,"meta":381,"style":381},"(Pdb) pp {k: type(v).__name__ for k, v in locals().items()}\n{'factor': 'float', 'percent': 'int', 'price': 'int'}\n(Pdb) p price * (1 - percent \u002F 100)\n85.0\n(Pdb) interact\n*interactive*\n>>> [apply_discount(p, 10) for p in (50, 75)]\n[45.0, 67.5]\n>>> exit()\n(Pdb)\n",[14,645,646,651,656,661,666,671,676,681,686,691],{"__ignoreMap":381},[385,647,648],{"class":255,"line":387},[385,649,650],{},"(Pdb) pp {k: type(v).__name__ for k, v in locals().items()}\n",[385,652,653],{"class":255,"line":393},[385,654,655],{},"{'factor': 'float', 'percent': 'int', 'price': 'int'}\n",[385,657,658],{"class":255,"line":399},[385,659,660],{},"(Pdb) p price * (1 - percent \u002F 100)\n",[385,662,663],{"class":255,"line":405},[385,664,665],{},"85.0\n",[385,667,668],{"class":255,"line":411},[385,669,670],{},"(Pdb) interact\n",[385,672,673],{"class":255,"line":417},[385,674,675],{},"*interactive*\n",[385,677,678],{"class":255,"line":424},[385,679,680],{},">>> [apply_discount(p, 10) for p in (50, 75)]\n",[385,682,683],{"class":255,"line":429},[385,684,685],{},"[45.0, 67.5]\n",[385,687,688],{"class":255,"line":435},[385,689,690],{},">>> exit()\n",[385,692,693],{"class":255,"line":604},[385,694,465],{},[355,696,698],{"id":697},"_4-navigate-frames-with-up-down-and-where","4. Navigate frames with up, down, and where",[10,700,701,702,84,704,707,708,84,710,713,714,125,716,718,719,721],{},"When the bug is in a caller, walk the stack. ",[14,703,143],{},[14,705,706],{},"u",") moves the current frame pointer toward the caller; ",[14,709,146],{},[14,711,712],{},"d",") moves back toward the callee. The interpreter stays paused at the original line — you are only changing which frame ",[14,715,10],{},[14,717,547],{},", and ",[14,720,131],{}," read from.",[376,723,725],{"className":378,"code":724,"language":380,"meta":381,"style":381},"# pipeline.py\ndef normalize(value):\n    return value.strip().lower()  # AttributeError if value is not a str\n\ndef ingest(records):\n    return [normalize(r) for r in records]\n\nif __name__ == \"__main__\":\n    ingest([\"A\", \"B\", 3])  # the int 3 breaks normalize\n",[14,726,727,732,737,742,746,751,756,760,764],{"__ignoreMap":381},[385,728,729],{"class":255,"line":387},[385,730,731],{},"# pipeline.py\n",[385,733,734],{"class":255,"line":393},[385,735,736],{},"def normalize(value):\n",[385,738,739],{"class":255,"line":399},[385,740,741],{},"    return value.strip().lower()  # AttributeError if value is not a str\n",[385,743,744],{"class":255,"line":405},[385,745,421],{"emptyLinePlaceholder":420},[385,747,748],{"class":255,"line":411},[385,749,750],{},"def ingest(records):\n",[385,752,753],{"class":255,"line":417},[385,754,755],{},"    return [normalize(r) for r in records]\n",[385,757,758],{"class":255,"line":424},[385,759,421],{"emptyLinePlaceholder":420},[385,761,762],{"class":255,"line":429},[385,763,432],{},[385,765,766],{"class":255,"line":435},[385,767,768],{},"    ingest([\"A\", \"B\", 3])  # the int 3 breaks normalize\n",[376,770,772],{"className":441,"code":771,"language":443,"meta":381,"style":381},"$ python -m pdb pipeline.py\n(Pdb) continue\nAttributeError: 'int' object has no attribute 'strip'\n> pipeline.py(2)normalize()\n-> return value.strip().lower()\n(Pdb) p value          # the offending value, in the callee frame\n3\n(Pdb) up               # move to ingest()\n> pipeline.py(5)ingest()\n-> return [normalize(r) for r in records]\n(Pdb) p records        # now we read the caller's locals\n['A', 'B', 3]\n",[14,773,774,779,784,789,794,799,804,809,814,819,824,830],{"__ignoreMap":381},[385,775,776],{"class":255,"line":387},[385,777,778],{},"$ python -m pdb pipeline.py\n",[385,780,781],{"class":255,"line":393},[385,782,783],{},"(Pdb) continue\n",[385,785,786],{"class":255,"line":399},[385,787,788],{},"AttributeError: 'int' object has no attribute 'strip'\n",[385,790,791],{"class":255,"line":405},[385,792,793],{},"> pipeline.py(2)normalize()\n",[385,795,796],{"class":255,"line":411},[385,797,798],{},"-> return value.strip().lower()\n",[385,800,801],{"class":255,"line":417},[385,802,803],{},"(Pdb) p value          # the offending value, in the callee frame\n",[385,805,806],{"class":255,"line":424},[385,807,808],{},"3\n",[385,810,811],{"class":255,"line":429},[385,812,813],{},"(Pdb) up               # move to ingest()\n",[385,815,816],{"class":255,"line":435},[385,817,818],{},"> pipeline.py(5)ingest()\n",[385,820,821],{"class":255,"line":604},[385,822,823],{},"-> return [normalize(r) for r in records]\n",[385,825,827],{"class":255,"line":826},11,[385,828,829],{},"(Pdb) p records        # now we read the caller's locals\n",[385,831,833],{"class":255,"line":832},12,[385,834,835],{},"['A', 'B', 3]\n",[355,837,839],{"id":838},"_5-control-execution-n-s-c-until-return","5. Control execution: n, s, c, until, return",[10,841,842],{},"These commands resume the interpreter, each with a different stop condition:",[39,844,845,860,874,884,894],{},[42,846,847,855,856,859],{},[45,848,849,84,851,854],{},[14,850,150],{},[14,852,853],{},"n",")"," — run the current line; stop at the next line in ",[137,857,858],{},"this"," function (calls run to completion).",[42,861,862,869,870,873],{},[45,863,864,84,866,854],{},[14,865,153],{},[14,867,868],{},"s"," — run until the next line ",[137,871,872],{},"anywhere",", descending into called functions.",[42,875,876,883],{},[45,877,878,84,880,854],{},[14,879,159],{},[14,881,882],{},"c"," — run until the next breakpoint or program end.",[42,885,886,893],{},[45,887,888,84,890,854],{},[14,889,156],{},[14,891,892],{},"unt"," — run until a line numerically greater than the current one is reached; with no argument it is the fast way to finish a loop without stepping every iteration.",[42,895,896,903],{},[45,897,898,84,900,854],{},[14,899,162],{},[14,901,902],{},"r"," — run until the current function is about to return, then stop on the return.",[376,905,907],{"className":441,"code":906,"language":443,"meta":381,"style":381},"(Pdb) n          # step over, stay in this frame\n(Pdb) s          # step into the next call\n(Pdb) until      # run out the rest of the current loop\n(Pdb) r          # run to this function's return\n(Pdb) c          # resume normally\n",[14,908,909,914,919,924,929],{"__ignoreMap":381},[385,910,911],{"class":255,"line":387},[385,912,913],{},"(Pdb) n          # step over, stay in this frame\n",[385,915,916],{"class":255,"line":393},[385,917,918],{},"(Pdb) s          # step into the next call\n",[385,920,921],{"class":255,"line":399},[385,922,923],{},"(Pdb) until      # run out the rest of the current loop\n",[385,925,926],{"class":255,"line":405},[385,927,928],{},"(Pdb) r          # run to this function's return\n",[385,930,931],{"class":255,"line":411},[385,932,933],{},"(Pdb) c          # resume normally\n",[355,935,937],{"id":936},"_6-watch-values-with-display","6. Watch values with display",[10,939,940,943,944,947,948,950,951,953],{},[14,941,942],{},"display expr"," registers an expression that pdb re-evaluates after every stop, printing it only when the value changes — a built-in watch window. ",[14,945,946],{},"undisplay"," clears it. This is the cleanest way to track a loop variable across ",[14,949,150],{}," steps without retyping ",[14,952,10],{},".",[376,955,957],{"className":441,"code":956,"language":443,"meta":381,"style":381},"(Pdb) display factor\ndisplay factor: 0.85\n(Pdb) display price * factor\ndisplay price * factor: 85.0\n(Pdb) n\ndisplay price * factor: 85.0   # reprinted only if it changed\n",[14,958,959,964,969,974,979,984],{"__ignoreMap":381},[385,960,961],{"class":255,"line":387},[385,962,963],{},"(Pdb) display factor\n",[385,965,966],{"class":255,"line":393},[385,967,968],{},"display factor: 0.85\n",[385,970,971],{"class":255,"line":399},[385,972,973],{},"(Pdb) display price * factor\n",[385,975,976],{"class":255,"line":405},[385,977,978],{},"display price * factor: 85.0\n",[385,980,981],{"class":255,"line":411},[385,982,983],{},"(Pdb) n\n",[385,985,986],{"class":255,"line":417},[385,987,988],{},"display price * factor: 85.0   # reprinted only if it changed\n",[355,990,992],{"id":991},"_7-drop-into-the-debugger-from-pytest","7. Drop into the debugger from pytest",[10,994,995,996,998],{},"pytest integrates ",[14,997,20],{}," directly, and disables output capturing automatically so the prompt is visible:",[39,1000,1001,1016,1028],{},[42,1002,1003,1008,1009,1011,1012,953],{},[45,1004,1005],{},[14,1006,1007],{},"pytest --pdb"," invokes post-mortem at the moment of failure — the prompt opens in the failing frame with the exception still live. This is the workhorse for ",[14,1010,16],{}," triage; the dedicated walkthrough lives in ",[550,1013,1015],{"href":1014},"\u002Fsystematic-debugging-performance-profiling\u002Finteractive-debugging-with-pdb-and-ipdb\u002Fpost-mortem-debugging-with-pdb-pm\u002F","post-mortem debugging with pdb.pm()",[42,1017,1018,1023,1024,1027],{},[45,1019,1020],{},[14,1021,1022],{},"pytest --trace"," stops at the first line of ",[137,1025,1026],{},"every selected test",", so you can step through setup interactively.",[42,1029,1030,1035,1036,28,1038,1040],{},[45,1031,1032],{},[14,1033,1034],{},"pytest --pdbcls=IPython.terminal.debugger:TerminalPdb"," swaps in the IPython debugger for ",[14,1037,101],{},[14,1039,97],{}," so failures land in an ipdb-style prompt.",[376,1042,1044],{"className":474,"code":1043,"language":476,"meta":381,"style":381},"pytest tests\u002Ftest_orders.py::test_discount --pdb        # post-mortem on failure\npytest tests\u002Ftest_orders.py::test_discount --trace      # break at test start\npytest --pdbcls=IPython.terminal.debugger:TerminalPdb --pdb\n",[14,1045,1046,1061,1073],{"__ignoreMap":381},[385,1047,1048,1051,1054,1058],{"class":255,"line":387},[385,1049,1050],{"class":494},"pytest",[385,1052,1053],{"class":490}," tests\u002Ftest_orders.py::test_discount",[385,1055,1057],{"class":1056},"sj4cs"," --pdb",[385,1059,1060],{"class":501},"        # post-mortem on failure\n",[385,1062,1063,1065,1067,1070],{"class":255,"line":393},[385,1064,1050],{"class":494},[385,1066,1053],{"class":490},[385,1068,1069],{"class":1056}," --trace",[385,1071,1072],{"class":501},"      # break at test start\n",[385,1074,1075,1077,1080],{"class":255,"line":399},[385,1076,1050],{"class":494},[385,1078,1079],{"class":1056}," --pdbcls=IPython.terminal.debugger:TerminalPdb",[385,1081,1082],{"class":1056}," --pdb\n",[10,1084,1085,1086,1090,1091,953],{},"When the breakpoint sits inside a fixture, the same setup ordering described in ",[550,1087,1089],{"href":1088},"\u002Fadvanced-pytest-architecture-configuration\u002Fmastering-pytest-fixtures\u002F","mastering pytest fixtures"," governs which frame you land in. For setting breakpoints that only fire on a specific iteration or condition, see ",[550,1092,1094],{"href":1093},"\u002Fsystematic-debugging-performance-profiling\u002Finteractive-debugging-with-pdb-and-ipdb\u002Fsetting-conditional-breakpoints-in-pdb\u002F","setting conditional breakpoints in pdb",[355,1096,1098],{"id":1097},"_8-ipdb-extras","8. ipdb extras",[10,1100,1101,1103,1104,1106,1107,1110,1111,1114,1115,1118],{},[14,1102,31],{}," is ",[14,1105,20],{}," with the IPython front end. Commands are identical; what you gain is tab completion on locals and attributes, syntax-highlighted source listings, multi-line paste support, and IPython magics such as ",[14,1108,1109],{},"%timeit"," at the prompt. Inside an IPython session or Jupyter, ",[14,1112,1113],{},"%debug"," opens a post-mortem ipdb prompt on the last exception, and ",[14,1116,1117],{},"%pdb on"," arms automatic post-mortem for the rest of the session.",[376,1120,1122],{"className":378,"code":1121,"language":380,"meta":381,"style":381},"import ipdb\n\ndef parse(payload):\n    ipdb.set_trace()      # identical to breakpoint() but with the ipdb UI\n    return payload[\"id\"]\n",[14,1123,1124,1129,1133,1138,1143],{"__ignoreMap":381},[385,1125,1126],{"class":255,"line":387},[385,1127,1128],{},"import ipdb\n",[385,1130,1131],{"class":255,"line":393},[385,1132,421],{"emptyLinePlaceholder":420},[385,1134,1135],{"class":255,"line":399},[385,1136,1137],{},"def parse(payload):\n",[385,1139,1140],{"class":255,"line":405},[385,1141,1142],{},"    ipdb.set_trace()      # identical to breakpoint() but with the ipdb UI\n",[385,1144,1145],{"class":255,"line":411},[385,1146,1147],{},"    return payload[\"id\"]\n",[34,1149,1151],{"id":1150},"verification","Verification",[10,1153,1154],{},"Confirm the debugger actually stopped where you intended rather than at an import-time side effect:",[39,1156,1157,1172,1183,1193],{},[42,1158,1159,1160,1162,1163,1166,1167,1169,1170,59],{},"At the prompt, run ",[14,1161,134],{}," — the arrow ",[14,1164,1165],{},"->"," must point at your ",[14,1168,24],{}," line (or the failing line under ",[14,1171,101],{},[42,1173,1174,1175,1178,1179,1182],{},"Run ",[14,1176,1177],{},"import sys; p sys.gettrace()"," — a non-",[14,1180,1181],{},"None"," trace function confirms the debugger is installed on the frame.",[42,1184,1185,1186,1189,1190,1192],{},"For pytest, run with ",[14,1187,1188],{},"-s"," once if the prompt does not appear; if ",[14,1191,1188],{}," is required, a plugin is interfering with pdb's auto-uncapture and should be reported.",[42,1194,1195,1196,1199,1200,1202],{},"Check the breakpoint hook is what you expect: ",[14,1197,1198],{},"python -c \"import sys; print(sys.breakpointhook)\""," should reflect your ",[14,1201,54],{}," setting.",[34,1204,1206],{"id":1205},"troubleshooting","Troubleshooting",[1208,1209,1210,1226],"table",{},[1211,1212,1213],"thead",{},[1214,1215,1216,1220,1223],"tr",{},[1217,1218,1219],"th",{},"Symptom",[1217,1221,1222],{},"Root cause",[1217,1224,1225],{},"Fix",[1227,1228,1229,1255,1274,1294,1309,1330],"tbody",{},[1214,1230,1231,1238,1241],{},[1232,1233,1234,1237],"td",{},[14,1235,1236],{},"(Pdb)"," prompt never appears under pytest",[1232,1239,1240],{},"Output capturing hid stdin\u002Fstdout",[1232,1242,610,1243,633,1245,1247,1248,1250,1251,1254],{},[14,1244,101],{},[14,1246,24],{}," (auto-uncaptured) or add ",[14,1249,1188],{},"; avoid raw ",[14,1252,1253],{},"print"," debugging",[1214,1256,1257,1262,1268],{},[1232,1258,1259,1261],{},[14,1260,24],{}," does nothing",[1232,1263,1264,1267],{},[14,1265,1266],{},"PYTHONBREAKPOINT=0"," set in the environment",[1232,1269,1270,1271],{},"Unset it, or set ",[14,1272,1273],{},"PYTHONBREAKPOINT=pdb.set_trace",[1214,1275,1276,1282,1285],{},[1232,1277,1278,1281],{},[14,1279,1280],{},"*** NameError"," when printing a variable",[1232,1283,1284],{},"You are in the wrong frame",[1232,1286,1287,633,1289,1291,1292],{},[14,1288,143],{},[14,1290,146],{}," to the frame that defines the name, then re-run ",[14,1293,10],{},[1214,1295,1296,1301,1304],{},[1232,1297,1298,1300],{},[14,1299,153],{}," never enters a C-implemented call",[1232,1302,1303],{},"C functions have no Python trace events",[1232,1305,610,1306,1308],{},[14,1307,150],{}," to step over; debug the Python wrapper instead",[1214,1310,1311,1320,1323],{},[1232,1312,1313,1316,1317],{},[14,1314,1315],{},"ipdb.set_trace()"," raises ",[14,1318,1319],{},"ImportError",[1232,1321,1322],{},"ipdb not installed in the active venv",[1232,1324,1325,1327,1328],{},[14,1326,87],{},", or fall back to ",[14,1329,24],{},[1214,1331,1332,1335,1341],{},[1232,1333,1334],{},"Loop is tedious to step through",[1232,1336,1337,1338,1340],{},"Using ",[14,1339,150],{}," per iteration",[1232,1342,610,1343,1345],{},[14,1344,156],{}," with no arg, or a conditional breakpoint on the exit line",[34,1347,1349],{"id":1348},"frequently-asked-questions","Frequently Asked Questions",[10,1351,1352,1355,1357,1358,1360,1361,1364,1365,1367],{},[45,1353,1354],{},"What is the difference between pdb and ipdb?",[14,1356,20],{}," is the standard-library debugger with a bare prompt. ",[14,1359,31],{}," wraps the same ",[14,1362,1363],{},"Pdb"," machinery in IPython, adding tab completion, syntax highlighting, better tracebacks, and richer introspection. Functionally the commands are identical; ",[14,1366,31],{}," only improves the interactive experience.",[10,1369,1370,1373,1375,1376,1378,1379,1381,1382,1384,1385,1388,1389,1391],{},[45,1371,1372],{},"How does breakpoint() know whether to launch pdb or ipdb?",[14,1374,24],{}," calls ",[14,1377,367],{},", which reads the ",[14,1380,54],{}," environment variable. By default it runs ",[14,1383,118],{},"; setting ",[14,1386,1387],{},"PYTHONBREAKPOINT=ipdb.set_trace"," routes to ipdb, and ",[14,1390,1266],{}," disables all breakpoints without touching the source.",[10,1393,1394,1397,1398,28,1400,1402,1403,1405],{},[45,1395,1396],{},"Why does pytest swallow my breakpoint() call?","\npytest captures stdout and stdin by default, which hides the debugger prompt. pytest detects pdb and disables capturing automatically for ",[14,1399,24],{},[14,1401,101],{},", but a custom hook or the ",[14,1404,1188],{}," flag may be needed if you use a third-party debugger that pytest does not recognize.",[10,1407,1408,1411,84,1413,1415,1416,84,1418,1420,1421,1423,1424,1426],{},[45,1409,1410],{},"What is the difference between the next and step commands?",[14,1412,150],{},[14,1414,853],{},") executes the current line and stops at the next line in the same function, treating calls as a single step. ",[14,1417,153],{},[14,1419,868],{},") descends into the called function and stops at its first line. Use ",[14,1422,153],{}," to inspect a callee and ",[14,1425,150],{}," to stay at the current level.",[34,1428,1430],{"id":1429},"related-guides","Related guides",[39,1432,1433,1439,1445,1452],{},[42,1434,1435,1436,1438],{},"Once a breakpoint fires too often, narrow it down with ",[550,1437,1094],{"href":1093}," so the prompt only opens on the iteration that matters.",[42,1440,1441,1442,1444],{},"When a crash has already happened, ",[550,1443,1015],{"href":1014}," reopens the exact failing frame without a re-run.",[42,1446,1447,1448,953],{},"Memory growth that the debugger cannot explain is a job for ",[550,1449,1451],{"href":1450},"\u002Fsystematic-debugging-performance-profiling\u002Fmemory-profiling-with-tracemalloc\u002F","memory profiling with tracemalloc",[42,1453,1454,1455,1458,1459,953],{},"Debugger prompts that hang inside ",[14,1456,1457],{},"await"," calls usually trace back to event-loop issues covered in ",[550,1460,1462],{"href":1461},"\u002Fsystematic-debugging-performance-profiling\u002Fdebugging-async-code-and-event-loops\u002F","debugging async code and event loops",[10,1464,1465,1466],{},"← Back to ",[550,1467,1469],{"href":1468},"\u002Fsystematic-debugging-performance-profiling\u002F","Systematic Debugging & Performance Profiling",[1471,1472,1473],"style",{},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":381,"searchDepth":393,"depth":393,"links":1475},[1476,1477,1478,1488,1489,1490,1491],{"id":36,"depth":393,"text":37},{"id":105,"depth":393,"text":106},{"id":352,"depth":393,"text":353,"children":1479},[1480,1481,1482,1483,1484,1485,1486,1487],{"id":357,"depth":399,"text":358},{"id":521,"depth":399,"text":522},{"id":626,"depth":399,"text":627},{"id":697,"depth":399,"text":698},{"id":838,"depth":399,"text":839},{"id":936,"depth":399,"text":937},{"id":991,"depth":399,"text":992},{"id":1097,"depth":399,"text":1098},{"id":1150,"depth":393,"text":1151},{"id":1205,"depth":393,"text":1206},{"id":1348,"depth":393,"text":1349},{"id":1429,"depth":393,"text":1430},"Master interactive Python debugging with pdb, ipdb, and breakpoint(): navigate frames, set breakpoints, inspect state, and drop into the debugger from pytest.","md",{"slug":1495,"type":1496,"breadcrumb":1497,"datePublished":1498,"dateModified":1498,"faq":1499,"howto":1508},"interactive-debugging-with-pdb-and-ipdb","cluster","pdb & ipdb","2026-06-18",[1500,1502,1504,1506],{"q":1354,"a":1501},"pdb is the standard-library debugger with a bare prompt. ipdb wraps the same Pdb machinery in IPython, adding tab completion, syntax highlighting, better tracebacks, and richer introspection. Functionally the commands are identical; ipdb only improves the interactive experience.",{"q":1372,"a":1503},"breakpoint() calls sys.breakpointhook(), which reads the PYTHONBREAKPOINT environment variable. By default it runs pdb.set_trace(); setting PYTHONBREAKPOINT=ipdb.set_trace routes to ipdb, and PYTHONBREAKPOINT=0 disables all breakpoints without touching the source.",{"q":1396,"a":1505},"pytest captures stdout and stdin by default, which hides the debugger prompt. pytest detects pdb and disables capturing automatically for breakpoint() and --pdb, but a custom hook or the -s flag may be needed if you use a third-party debugger that pytest does not recognize.",{"q":1410,"a":1507},"next (n) executes the current line and stops at the next line in the same function, treating calls as a single step. step (s) descends into the called function and stops at its first line. Use step to inspect a callee and next to stay at the current level.",{"name":1509,"description":1510,"steps":1511},"How to debug Python interactively with pdb and ipdb","Set a breakpoint, drop into the debugger, navigate the call stack, and inspect state to find a defect.",[1512,1515,1518,1521],{"name":1513,"text":1514},"Insert a breakpoint","Call breakpoint() on the line you want to pause at, or pass --trace to pytest to stop at the first line of every test.",{"name":1516,"text":1517},"Inspect the current frame","Use list, where, and the print and pp commands to view source context, the call stack, and variable values at the pause point.",{"name":1519,"text":1520},"Navigate the stack and step execution","Move between frames with up and down, then advance with next, step, until, and continue to reproduce the defect path.",{"name":1522,"text":1523},"Route through ipdb when richer introspection helps","Set PYTHONBREAKPOINT=ipdb.set_trace to get tab completion and syntax highlighting without editing source, then re-run.","\u002Fsystematic-debugging-performance-profiling\u002Finteractive-debugging-with-pdb-and-ipdb",{"title":5,"description":1492},"systematic-debugging-performance-profiling\u002Finteractive-debugging-with-pdb-and-ipdb\u002Findex","mo666YzB9M5BItRoLWKAD2tzxH1xjbbscpefi1MNGNQ",1781793487030]