Unit 07 · lesson

Read the Evidence

Core path: 30 minutes

When Python fails, what information is it already giving you?

By Week 7, you have seen enough errors that the red text should no longer feel like an emergency alarm.

An error report is evidence from the runtime.

It may not explain the entire cause. It does tell you something concrete about where execution failed, what Python was trying to do, and what kind of problem it detected.

Expected versus actual is the debugging gap

Every investigation begins with a mismatch:

EXPECTED
what you thought the program would do

ACTUAL
what you observed it do

The useful question is:

Where is the first point in the execution where the actual state stops matching the expected state?

That point may appear before the line where the failure finally becomes visible.

Three broad failure categories

Syntax errors

Python cannot successfully parse the program structure.

if score >= 70
    print("pass")

The missing colon prevents the program from being interpreted as valid Python structure.

The intended branch never begins executing normally.

Use an intentionally broken source file and read the parser evidence before repairing it:

syntax_error.py
OutputRun with button or Ctrl/Cmd+Enter
Run the code to see output.
Ready to edit. Press Run when you want evidence.

Run this intentionally broken source before fixing it. Use the error location and message as evidence that Python rejected the source structure before normal execution.

Runtime errors

The code is structurally valid enough to begin execution, but an operation fails while the program is running.

age = int("fifteen")

The call is valid Python syntax. The conversion fails because the value cannot be interpreted as an integer.

Now inspect a runtime failure where the bad representation enters before the visible error:

type_error_evidence.py

One line is returned for each input() call, in order.

OutputRun with button or Ctrl/Cmd+Enter
Run the code to see output.
Ready to edit. Press Run when you want evidence.

Run before repairing it. Read the final exception first, then use the printed value and type to explain why the failing division receives incompatible operands.

Logic errors

The program runs without Python necessarily raising an exception, but the behavior is wrong.

if score >= 70:
    rank = "PASS"
elif score >= 90:
    rank = "EXCELLENT"

A score of 95 enters the first branch, so the EXCELLENT branch never runs.

No red traceback appears.

That is why debugging cannot mean "fix the red text."

What a traceback actually is

Run:

score = int(input("Score: "))
average = score / 0
print(average)

Python produces a traceback ending with something like:

ZeroDivisionError: division by zero

The traceback usually gives several useful categories of evidence:

exception type
message
file path
line number
execution frames that led there
source line or expression

Do not treat the whole traceback as one giant wall of text.

Extract the pieces.

Read from the bottom, then follow the path upward

For a beginner, a useful first pass is:

  1. exception type and final message;
  2. the line in your code where the failure surfaced;
  3. the file and line number;
  4. then earlier frames if the call traveled through functions.

The bottom tells you what Python finally reported.

The earlier frames help explain how execution reached that point.

As programs become more layered, that call path matters more.

Use the nested example below without fixing it first. Read the final exception, then reconstruct the call path from the traceback frames:

traceback_path.py
OutputRun with button or Ctrl/Cmd+Enter
Run the code to see output.
Ready to edit. Press Run when you want evidence.

Do not fix the zero yet. Read the traceback as an execution path: top-level call → build_report() → calculate_average() → failing division. Then change count to a valid value and verify the path completes.

The line that fails is not always the line that caused the bad state

Consider:

temperature = input("Temperature: ")
adjusted_temperature = temperature + 5

The visible failure appears on the second line.

But the bad state entered earlier:

input() returned a string

temperature refers to text

program attempts str + int

TypeError becomes visible

Changing the + 5 expression randomly attacks the symptom without understanding the data that reached it.

The better investigation asks:

What value and type did temperature have before the failing operation?

Error type narrows the question, not the whole answer

A NameError often suggests Python could not resolve a name.

A TypeError often suggests an operation received incompatible types or an unsupported combination.

A ValueError can indicate the type of operation was valid but the specific value was unacceptable.

A FileNotFoundError tells you a requested path did not resolve to an existing file at that moment.

Do not memorize those as magical diagnoses.

Use them to decide what evidence to inspect next.

For example:

FileNotFoundError

inspect path + working directory
TypeError

inspect values + types at failing operation

A traceback can cross function boundaries

def calculate_average(total, count):
    return total / count


def build_report():
    average = calculate_average(100, 0)
    return average

print(build_report())

The failure occurs inside calculate_average(), but the traceback can show how the call came from build_report() and then from the top-level program.

That call stack is a path through the running program.

Later you will use the debugger to walk the same kind of path interactively.

Extract evidence before proposing a fix

Given:

count = input("Device count: ")
print(100 / count)

Suppose the user enters 4.

Before changing code, record:

actual input:
value stored in count:
type of count:
failing expression:
exception type:
hypothesis:

Then test the hypothesis with:

print(repr(count))
print(type(count))

Only after you confirm the representation should you choose the repair.

Logic errors need different evidence

Suppose:

battery = 90

if battery >= 20:
    status = "NORMAL"
elif battery >= 80:
    status = "HIGH"

There may be no exception.

So your evidence has to come from:

input value
condition order
condition results
branch actually selected
expected branch

That is why Week 4's execution-path model matters again here.

Debugging reuses earlier mental models instead of replacing them.

Success check

Given a failure, you should be able to say something more precise than:

It doesn't work.

A useful report sounds like:

With input 4, execution reaches line 2 and raises a TypeError because count is the string '4' while the division operation requires a numeric operand. type(count) confirms the value is str.

That statement contains reproduction conditions, location, error category, and state evidence.

Now you have something worth debugging.

Vocabulary lab

Flip the idea, not just the card

Explain the term before you reveal the back. Then compare your explanation with the definition, example, and warning.

1 / 5
Read all terms without animation
Syntax Error
A failure caused by source code that does not form valid Python syntax. Example: A missing colon after an if statement. Do not confuse it with: A runtime failure that occurs after execution begins.
Runtime Error
A failure that occurs while structurally valid code is executing. Example: Division by zero or converting 'fifteen' with int(). Do not confuse it with: A logic error that may produce wrong behavior without an exception.
Logic Error
A defect where the program executes but produces behavior that does not match the requirement. Example: A broad branch placed before a more specific threshold branch. Do not confuse it with: A syntax error that prevents normal execution.
Traceback
Python's structured report showing an exception and the execution frames involved in reaching it. Example: A ZeroDivisionError traceback with file and line information. Do not confuse it with: A guess about what caused the failure.
Symptom
The visible failure or incorrect behavior that reveals a problem exists. Example: A TypeError on temperature + 5. Do not confuse it with: The earlier state or decision that caused the symptom.