Editor and debugger¶
~14 min
In short¶
An editor is where you write code. A debugger pauses your program so you can inspect its variables one step at a time. This page covers print debugging, the Python debugger pdb, the VS Code debugger, and how to read a stack trace.
VS Code basics¶
Install Visual Studio Code and the Python extension. The debugger steps below use that extension [1]. The window has five parts [2]:
- Activity bar: the narrow strip on the far left. It switches between views such as the file explorer.
- Primary side bar: holds views such as the Explorer.
- Editor: where you edit files. You can split it side by side.
- Panel: below the editor. It holds program output, problems, and the integrated terminal.
- Status bar: the strip at the bottom with project and file information.
The Command Palette runs any VS Code command by name. It opens with Ctrl+Shift+P on Windows and Linux, or Shift+Cmd+P on macOS [2].
Open a folder with File > Open Folder so the Explorer shows the whole project. Put each project under Git, as described in Git and GitHub.
Method 1: print debugging¶
Print debugging means adding a print line where you want to see a value. It is quick and works in any language. Here is a buggy program. Its average is wrong, and it crashes on an empty list.
$ cat average.py
def average(numbers):
total = 0
for n in numbers:
total += n
return total / len(numbers) - 1
...
$ python3 average.py
average: 79.0
The average of 70, 80, and 90 should be 80.0, not 79.0. Add a print line to see the values:
$ python3 average_print.py
DEBUG total= 240 n= 3
average: 79.0
DEBUG total= 0 n= 0
Traceback (most recent call last):
...
ZeroDivisionError: division by zero
The printed total is 240 and the count is 3, so the sum is correct. The problem is the - 1 at the end of the return line. Remove the debug line when you are done, because stray prints clutter your output.
Method 2: the Python debugger, pdb¶
breakpoint() pauses the program at that line and opens pdb, the Python debugger [3]. In Python 3.7 and later, you do not need to import anything. Inside pdb, you type short commands at the (Pdb) prompt [3]:
n(next): run the current line, then stop at the next line in this function.s(step): run the current line and stop inside any function it calls.p expression: print the value of an expression.c(continue): run until the next breakpoint or the end of the program.l(list): show the source lines around the current line.q(quit): stop the program.
The lab version of the buggy program calls breakpoint() inside average. Here are the commands, fed in from a file so the session can be shown:
$ printf 'p total\np len(numbers)\nn\np total / len(numbers)\nc\n' | python3 average_bp.py
> /private/tmp/cs-lab/debug/average_bp.py(5)average()
-> breakpoint()
(Pdb) 240
(Pdb) 3
(Pdb) > /private/tmp/cs-lab/debug/average_bp.py(6)average()
-> return total / len(numbers) - 1
(Pdb) 80.0
(Pdb) average: 79.0
The p commands show total as 240 and len(numbers) as 3. After n, the debugger is on the return line. p total / len(numbers) gives 80.0, which is the right average. The bug is the - 1 after it.
Method 3: the VS Code debugger¶
The VS Code debugger does the same job with keys and panels. The steps below follow the VS Code documentation [1][4]. They were not run on screen for this page.
- Click the editor margin to the left of a line number, or put the cursor on the line and press
F9, to set a breakpoint [4]. - Press
F5to start the debugger. Choose the Python configuration when VS Code asks [1]. - When the program stops, use
F10(Step Over) to run the current line without entering the functions it calls. UseF11(Step Into) to go into the function on that line [4]. - Watch the VARIABLES panel. It shows local and global variables for the stack frame you have selected [4]. You can also change a value there during the session.
Reading a stack trace¶
When a program crashes, Python prints a traceback. Here is the crash from the buggy program, copied from the lab run:
Traceback (most recent call last):
File "/private/tmp/cs-lab/debug/average.py", line 15, in <module>
print(report([]))
~~~~~~^^^^
File "/private/tmp/cs-lab/debug/average.py", line 9, in report
avg = average(scores)
File "/private/tmp/cs-lab/debug/average.py", line 5, in average
return total / len(numbers) - 1
~~~~~~^~~~~~~~~~~~~~
ZeroDivisionError: division by zero
Read it in this order [5]:
- The last line is the error type and message:
ZeroDivisionError: division by zero. - The line just above it is the frame where the error happened:
average.py, line 5. - Go up one frame at a time to see the calls that led there. Here,
reportcalledaverageat line 9, and the module calledreportat line 15. - The
^^^^and~~~marks under each line point at the expression that failed. Python 3.11 and later print them [5].
The top of the traceback is the outermost call. The bottom is where the error was raised. That is why the most recent call is printed last. Line 15 passes an empty list, so len(numbers) is 0 inside average. That is the clue for the fix.
Fix the bug¶
A correct version checks the input and removes the stray - 1. The check below runs the function on normal and empty input:
def average(numbers):
if not numbers:
raise ValueError("average() needs at least one number")
return sum(numbers) / len(numbers)
assert average([70, 80, 90]) == 80
assert average([5]) == 5
try:
average([])
except ValueError as error:
assert "at least one number" in str(error)
else:
raise AssertionError("expected ValueError for an empty list")
Try this¶
- Save the buggy average program in a folder under
/tmp. Run it, then add abreakpoint()and step through it withnandp. - Set one breakpoint in VS Code with
F9, start withF5, and watchtotalchange in the Variables panel. - Read the Python course's reading-errors lesson and check that you can explain each line of a traceback.
Sources¶
- [1] VS Code documentation, "Python debugging". https://code.visualstudio.com/docs/python/debugging
- [2] VS Code documentation, "User interface". https://code.visualstudio.com/docs/getstarted/userinterface
- [3] Python 3.12 documentation, "pdb: The Python Debugger". https://docs.python.org/3.12/library/pdb.html
- [4] VS Code documentation, "Debugging". https://code.visualstudio.com/docs/editor/debugging
- [5] Python 3.12 documentation, "traceback". https://docs.python.org/3.12/library/traceback.html