Debugging with Print Statements and Tracebacks
Try/except structures prevent program crashes by catching exceptions and executing recovery code when operations fail.
A File Name Is an Assumption
When a program opens a file, it assumes that the file exists and can be read. That assumption may be wrong: a user may enter a filename that does not exist, or the file may be locked by another program. Without error handling, the program can crash and display a traceback that confuses the user. A try/except structure lets the program anticipate the failure, provide useful feedback, and stop cleanly when the file cannot be opened.
Treat the open() operation as a potential failure point rather than assuming it will always succeed.
Two Paths Through try and except
The try block contains the operation that may fail. If open() succeeds, the file object is assigned to fhand, the except block is skipped, and the program continues to the for loop that counts lines beginning with Subject. If open() raises a FileNotFoundError, Python stops executing the try block immediately and moves to the except block. The later for loop and result print do not execute.
What do you think happens?
A user enters a filename that does not exist. Which part runs next?
Reveal answer
Answer: The except block
The open() call raises a FileNotFoundError. Python immediately stops the try block and jumps to the except block, so the later loop and result print never execute.
Recovery Code That Protects the Rest
The except block is not only a place to report a problem. It is the recovery path for the failed operation. In this file example, recovery has two parts: print a message that identifies the file the user tried to open, then call exit() because opening the file was a critical operation. Exiting prevents the program from attempting to use an unopened file.
Tracing a Missing File
The user enters missing.txt, and that file does not exist.
1. Read the filename: The program receives missing.txt as the filename to open.
2. Attempt open(): The open() operation fails and raises FileNotFoundError.
3. Enter except: Python stops the try block and transfers control to the except block.
4. Give feedback: The program prints a message identifying missing.txt as the file that cannot be opened.
5. Stop safely: exit() terminates the program, so the for loop and final count do not run.
The user receives a meaningful file-opening message instead of the program continuing without an opened file.
Raw Tracebacks and Helpful Messages
Without error handling, Python displays a raw traceback. The source describes this as a technical message full of internal details that can confuse most users. With try/except, the program controls what the user sees. A message such as File cannot be opened: missing.txt identifies both the problem and the filename involved.
Print statements can also support tracing while you study the control flow: place a visible message before the operation and another message in the recovery path. The important question is what the output tells you about the path taken. If the opening fails, execution reaches the except message and then exits; it does not reach the later file-processing statements.
Mistakes That Break Recovery
Putting open() outside the try block
The file-opening operation is the point that can fail, so a failure happens before the try block can handle it.
Fix:
Wrap the open() operation itself in the try block.Continuing after a critical opening failure
The program may attempt to use a file that was never opened.
Fix:
Call exit() in the except block when opening the file is required for the rest of the program.Showing only the raw traceback to the user
The traceback contains technical details that can confuse the user.
Fix:
Catch the failure and print a user-friendly message that identifies the file.Giving feedback without identifying the filename
The user is not told which attempted file caused the problem.
Fix:
Include the filename in the recovery message, such as File cannot be opened: missing.txt.
Practice the Two Outcomes
Trace the file-reading program for two inputs: first, a valid filename that exists; second, a filename that does not exist. For each input, list which statements run, whether the except block runs, whether the for loop runs, and what the user sees.
Hints
- For an existing file, open() succeeds and fhand receives the file object.
- For a missing file, open() raises FileNotFoundError.
- Remember that exit() prevents the later loop and result print from running after the failure.
| Situation | open() | except block | Later loop and result |
|---|---|---|---|
| Filename exists | Succeeds; fhand is assigned | Skipped | Run |
| Filename does not exist | Raises FileNotFoundError | Runs, prints feedback, then calls exit() | Do not run |
The two control-flow outcomes for the file-opening operation.
Reliable File-Opening Practice
Place the file-opening operation in a try block, catch the opening failure in an except block, print a message that names the file, and call exit() when the rest of the program depends on the file being opened. This sequence keeps the failure from becoming an unexplained crash and prevents later code from using an unopened file.
- The main debugging question is not only whether an operation failed, but where control went afterward. For a successful opening, execution continues with file processing. For a failed opening, execution moves to recovery code, gives the user useful feedback, and exits before later file-dependent code runs.
Key Takeaways
- Opening a file is an assumption and a potential point of failure.
- A successful open() assigns the file object and allows later file-processing code to run.
- A failed open() raises FileNotFoundError and transfers control to the except block.
- The except block should provide a user-friendly message that identifies the filename.
- When file opening is critical, exit() prevents later code from using an unopened file.