PCAP Exceptions and File I/O Practice Question
A developer wants to ensure that a file is always closed, even if an exception occurs, without using the 'with' statement. Which approach correctly achieves this?
⚠ Common exam trap
Python Institute often tests the distinction between `else` and `finally` in exception handling, trapping candidates who think `else` runs unconditionally or that placing `open()` inside the `try` block is safe without checking for assignment failure.
Answer choices
Why each option matters
Answer the question above first, then reveal the full breakdown to understand why each option is right or wrong.
Correct answer & explanation
✓
f = open('file.txt'); try: ... finally: f.close()
The `finally` block is guaranteed to execute regardless of whether an exception occurs in the `try` block. This ensures that `f.close()` is always called, properly releasing the file resource. The `with` statement is not used, but the `try...finally` construct provides the same deterministic cleanup behavior.
Answer analysis
Option-by-option breakdown
For each option: why learners choose it and why it is or isn't the right answer here.
- ✗
f = open('file.txt'); try: ... except: ... else: f.close()
Why it's wrong here
The `try`/`except`/`else` structure routes cleanup to the `else` block, which executes only when the `try` block completes without raising an exception. If an exception occurs during processing, control jumps directly to the `except` block and the `else` block is skipped entirely, leaving the file object `f` open and its resources unreleased. Furthermore, the bare `except:` catches all exceptions, including `KeyboardInterrupt` and `SystemExit`, but still does not guarantee closure for those paths. This design conflates normal-path cleanup with exception-path handling, so it fails the requirement of always closing the file.
- ✓
f = open('file.txt'); try: ... finally: f.close()
Why this is correct
Using `try: ... finally: f.close()` is the canonical Python idiom because the `finally` block is guaranteed to execute whether or not an exception is raised inside the `try` block. Unlike `except`, which only handles exception paths, `finally` runs on normal completion, on exception propagation, and even when a `break`, `continue`, or `return` is encountered in the `try` body. This ensures that the file descriptor is released deterministically, preventing resource leaks in both expected and exceptional control flows. It is the building block of context managers, where the `with` statement uses `__exit__` to achieve similar deterministic cleanup.
- ✗
if f.closed: pass else: f.close()
Why it's wrong here
The `if f.closed: pass else: f.close()` approach performs a state check only after the processing block has already run, but it does nothing to handle exceptions that might occur during that processing. If an exception is raised inside the `...` section, control exits the current scope immediately, bypassing the `if` check entirely and leaving the file open. Moreover, `f.closed` is merely a status flag; it does not trigger any cleanup mechanism, and relying on it after the fact does not mitigate resource leaks from interrupted operations. This pattern is also fragile because it assumes `f` is already defined and bound, which is not guaranteed if the file-opening line itself fails or is skipped.
- ✗
try: f = open('file.txt'); ... except: pass; finally: f.close()
Why it's wrong here
The `try: f = open('file.txt'); ... except: pass; finally: f.close()` pattern is dangerous because the `open()` call is inside the `try` block; if `open()` raises an exception (e.g., `FileNotFoundError` or `PermissionError`), the assignment to `f` never happens, leaving `f` unbound. The `finally` block then attempts to call `f.close()`, which raises a `NameError` because `f` does not exist, masking the original exception and potentially crashing the program. Even if `open()` succeeds, the bare `except: pass` silently swallows any processing exceptions, which can hide critical bugs, while the `finally` block still runs — but the core flaw is the unbound variable on open failure. A safer pattern would assign `f = None` before the `try`, or use `with open(...) as f:` to avoid managing the lifecycle manually.
Go deeper
Related to this question
About these practice questions
One of 421 original PCAP practice questions on Courseiva, each with a full explanation and wrong-answer analysis — not exam dumps or protected exam content. Learn why practice questions differ from exam dumps →
JA
Written by Johnson Ajibi, MSc IT Security
Senior Network & Security Engineer · founder of Courseiva
This PCAP practice question is part of Courseiva's free Python Institute certification practice question bank. Courseiva provides original exam-style practice questions with explanations, topic-based practice, mock exams, readiness tracking, and study analytics to help learners prepare for the PCAP exam.