Learn
02.5 | TRACING, ERRORS AND DEBUGGING
01 | A FAILURE IS EVIDENCE
A program can fail in several ways. It may not start because its syntax is invalid. It may start and raise an exception during execution. It may run to completion but produce the wrong answer.
These are different problems, so they need different questions. What message appeared? Which statement was executing? What output did you expect, and what actually appeared?
Debugging means finding the cause and correcting it. Random edits can conceal the evidence. Begin with the smallest input that reliably reproduces the issue, then change one relevant thing at a time.
02 | SYNTAX ERRORS PREVENT PARSING
Syntax describes the valid structure of code. Missing colons, unmatched quotes or brackets, and malformed statements can raise SyntaxError. IndentationError identifies a problem with block structure.
In a saved script, Python parses the source before executing its statements. A syntax error later in the file can therefore prevent earlier statements from running too.
The displayed location is a clue, not a promise that the missing character is exactly there. Look immediately before the marker as well, especially for an unclosed quote or parenthesis.
score = 12
if score >= 10:
print("Target reached")03 | RUNTIME EXCEPTIONS IDENTIFY AN INVALID OPERATION
An exception can interrupt execution after the program has started. NameError can mean a misspelled or not-yet-defined name. TypeError can mean an operation used incompatible kinds of values.
ValueError occurs when a conversion receives unsuitable content, such as int("three"). ZeroDivisionError identifies division by zero. Read the exception type and its message together.
A traceback lists the execution path associated with the exception. Start near its final message and locate the referenced source line. Do not confuse the editor’s line number with the user’s input value.
04 | LOGIC ERRORS CAN RUN QUIETLY
A logic error gives incorrect intended behaviour without necessarily raising an exception. first + second / 2 runs, but it does not compute the mean of both numbers.
Similarly, range(1, 5) visits one through four. If the requirement was one through five, the code’s syntax is valid but its excluded stop is wrong.
You need an expected answer to detect these problems. “It ran” is weaker evidence than “it returned ten for inputs eight and twelve, matching the predicted mean”.
first = 8 second = 12 mean = (first + second) / 2 print(mean)
EXPECTED OUTPUT
10.0
05 | DRY-RUN THE EXECUTED INSTRUCTIONS
A dry run follows a program by hand without executing it. Record current values after relevant statements and note which conditions are tested.
For the total below, number takes one, two and three. The total starts zero and becomes one, three and six. The final print occurs after the loop.
Do not update a variable for a skipped statement. In an if/elif chain, a later condition after a match is not tested; that differs from a condition evaluated False.
total = 0
for number in range(1, 4):
total += number
print(total)EXPECTED OUTPUT
6
06 | USE A TRACE TABLE TO EXPOSE STATE
A useful trace table names the changing values and records meaningful execution steps. For a while loop, include the condition test, body action and update so the termination is visible.
Starting count at one with count <= 3, the tests at one, two and three are True and lead to output. After the update to four, the test is False and no output occurs for that case.
A trace is evidence about a particular input. To establish wider correctness, test additional routes and boundaries rather than assuming one trace covers every possibility.
07 | ADD TEMPORARY DIAGNOSTIC PRINTS
A diagnostic print displays internal values to help locate a fault. For a total, print the counter and total after each addition. If the total restarts on every iteration, this reveals a wrongly placed reset.
Label diagnostic output so you can distinguish it from the intended final report. Remove or disable it after the correction unless it is an intentional testing feature.
A diagnostic print observes a value; it does not automatically repair the logic. Compare the observed sequence with the dry-run prediction.
total = 0
for number in range(1, 4):
total += number
print("DEBUG number:", number, "total:", total)
print("Final total:", total)08 | BREAKPOINTS LET YOU PAUSE AND INSPECT
A debugger can pause at a breakpoint, a marked source location. You can inspect variable values and step through execution rather than adding many temporary print statements.
In IDLE, its debugger and breakpoint controls let you follow execution. In VS Code, use the Python debugging workflow to run the file with breakpoints and inspect Variables. Tool layout can vary; use the official guides linked below.
Stepping is useful for checking which branch is reached and whether an update executes. Observe a small case first so you know what state to expect.
09 | HANDLE A KNOWN CONVERSION FAILURE
A try statement runs a block that may raise an exception. A matching except block handles the named exception. This is exception handling, a controlled response to a failure you expect and understand.
Here only the conversion belongs inside try. If int raises ValueError, the message runs. The optional else block runs if that try block finishes without an exception. This else belongs to try, not to an if.
Catching ValueError does not check the score’s range. Keep conversion and range validation as separate responsibilities. Avoid a broad catch that hides unrelated programming faults.
text = input("Whole-number score: ")
try:
score = int(text)
except ValueError:
print("Enter a whole number, such as 12")
else:
print("Converted score:", score)10 | WATCH A CORRECT AND BROKEN TOTAL
Compare the two fixed traces. Both iterate over one, two and three; the broken version resets total inside the body. Predict the final output before stepping.
11 | TEST CHANGES WITH EXPECTED RESULTS
Choose normal cases, exact boundaries and relevant invalid inputs. For a 0-to-100 score rule, -1, 0, 100 and 101 test the range transition. Add a word such as eighty to test conversion separately.
Record expected output, actual output and the interpretation. If invalid input is meant to be rejected, rejection is a successful test only when it is the intended controlled behaviour, not an unexplained crash.
After repairing one fault, rerun the earlier passing cases. This regression check looks for a correction that accidentally breaks another route.
12 | YOUR DEBUGGING INVESTIGATION
Repair a score band chain that tests >= 50 before >= 80, a total reset inside its loop, and a while counter that never updates. For each, write the cause, corrected source and a test proving the intended result.
Trace the infinite-loop fault rather than running it unbounded. Explain what stays unchanged and why the condition stays true.
Extension: add try/except ValueError around an integer input. Test valid numeric text and unsuitable text, keeping the conversion handler narrowly focused.
PUT YOUR SKILLS TO WORK
Use the Section 2 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.
Section 2 challenges Python course navigation