How do programs handle errors gracefully and read and write files without crashing?
Handle runtime errors with try and except, and read from and write to text files safely in Python
A focused answer to the H2 Computing outcome on exceptions and files. The try, except, else and finally blocks, raising exceptions, and reading and writing text files safely with the with statement.
Reviewed by: AI editorial process; not yet individually human-reviewed
Have a quick question? Jump to the Q&A page
Jump to a section
What this dot point is asking
SEAB wants you to handle runtime errors with try and except, and to read from and write to text files safely in Python. The central idea is that programs meet conditions they cannot fully control - missing files, bad input - and robust code anticipates these, recovering gracefully rather than crashing.
The answer
Exceptions and the try statement
An exception is a runtime error that interrupts normal flow (dividing by zero, a missing file, bad conversion). The try/except statement lets you catch and handle it:
try:
value = int(user_input) # might raise ValueError
except ValueError:
print("That was not a whole number.")
The try block holds risky code; if an exception of the named type occurs, control jumps to the matching except, and the program continues rather than terminating.
else and finally
The full statement has optional else and finally clauses:
try:
f = open("data.txt")
except FileNotFoundError:
print("File missing")
else:
print("Opened successfully") # runs only if no exception
finally:
print("Cleanup") # always runs
else runs only when the try succeeded; finally runs always, used for cleanup that must happen whatever the outcome.
Catching the right exception
Catch specific exception types you expect and can handle. A bare except: that catches everything also swallows bugs, typos and unexpected failures, hiding problems and complicating debugging. Be precise:
except (FileNotFoundError, ValueError): # only what you anticipate
...
You can also raise an exception deliberately to signal an error: raise ValueError("amount must be positive").
Reading and writing files
Open a file with open(name, mode) - mode "r" to read, "w" to write (overwrites), "a" to append. The with statement is preferred because it automatically closes the file, even if an error occurs:
with open("out.txt", "w") as f: # auto-closes on exit
f.write("Hello\n")
with open("in.txt", "r") as f:
for line in f:
print(line.strip())
Without with, a forgotten close() can leak file handles or leave data unsaved.
Examples in context
Example 1. Loading a configuration file. A program reads its settings from a file but must still start if the file is missing, so it wraps the read in try/except FileNotFoundError, falling back to sensible defaults. Users never see a crash; the program degrades gracefully to a default configuration.
Example 2. Processing a data file line by line. A script totals numbers from a file, skipping any line that is not a valid number by catching ValueError per line inside the loop. The with statement ensures the file closes even if processing aborts midway, so no handle is leaked and partial work is safely flushed.
Try this
Q1. What is the purpose of the finally block? [1 mark]
- Cue. It runs whether or not an exception occurred, for cleanup that must always happen (such as closing a resource).
Q2. Why is catching a specific exception better than a bare except? [2 marks]
- Cue. It handles only anticipated errors; a bare except also swallows bugs and unexpected failures, hiding problems and hindering debugging.
Q3. State one advantage of opening a file with the with statement. [1 mark]
- Cue. The file is closed automatically when the block ends, even if an exception is raised, preventing leaked handles.
Exam-style practice questions
Practice questions written in the style of SEAB exam questions on this dot point, with worked answer explainers. The year tag is the paper they imitate, not the source.
Original6 marksWrite a Python function `read_number(filename)` that reads a single integer from a text file and returns it, returning 0 if the file does not exist or does not contain a valid integer. Explain how exception handling makes this robust.Show worked answer →
def read_number(filename):
try:
with open(filename, "r") as f:
return int(f.read().strip())
except FileNotFoundError:
return 0
except ValueError:
return 0
Exception handling makes this robust by catching the specific failures rather than letting them crash the program: FileNotFoundError if the file is missing, and ValueError if the contents cannot be converted to an integer. In either case the function returns a safe default of 0. The with statement guarantees the file is closed even if an error occurs.
Markers reward the try/except around the risky operations, catching the two specific exceptions (not a bare except), the safe default return, and the with statement for safe file handling.
Original5 marks(a) Explain the purpose of the `finally` block in a try statement. (b) Why is catching a specific exception type preferable to a bare `except` that catches everything? (c) State why the `with` statement is preferred for opening files.Show worked answer →
(a) The finally block runs whether or not an exception occurred - after the try (and any except) completes. It is used for cleanup that must always happen, such as releasing a resource or closing a connection, even when an error is raised.
(b) Catching a specific exception means you only handle the errors you anticipate and know how to recover from; a bare except silently swallows every error, including bugs and unexpected failures (even typos or interrupts), hiding problems and making debugging far harder.
(c) The with statement opens a file as a context manager and automatically closes it when the block ends, even if an exception is raised inside. This prevents leaked file handles and unsaved data that a forgotten close() would cause.
Markers reward finally always running for cleanup, specific exceptions avoiding the masking of unexpected errors, and with guaranteeing the file closes.
Related dot points
- Use Python selection, iteration and functions with parameters and return values to structure a solution, applying scope correctly
A focused answer to the H2 Computing outcome on Python control flow. Selection with if and elif, iteration with for and while, defining functions with parameters and return values, and local versus global scope.
- Define classes with attributes and methods, create objects, and apply encapsulation, inheritance and polymorphism in Python
A focused answer to the H2 Computing outcome on object-oriented programming. Classes and objects, attributes and methods, the constructor, and the principles of encapsulation, inheritance and polymorphism in Python.
- Design test cases using normal, boundary and erroneous data, distinguish levels of testing, and apply systematic debugging techniques
A focused answer to the H2 Computing outcome on testing and debugging. Choosing normal, boundary and erroneous test data, unit and integration and system testing, black-box versus white-box, and systematic debugging.
- Apply decomposition, modularity and abstraction to structure software, explaining the benefits for maintenance and reuse
A focused answer to the H2 Computing outcome on decomposition and abstraction. Breaking problems into modules, the role of interfaces, information hiding through abstraction, and the benefits for maintainability and reuse.
- Define database schemas with CREATE TABLE, choosing data types and enforcing PRIMARY KEY, FOREIGN KEY, NOT NULL and UNIQUE constraints
A focused answer to the H2 Computing outcome on defining schemas. CREATE TABLE, choosing appropriate data types, and enforcing primary key, foreign key, NOT NULL and UNIQUE constraints to protect data integrity.