Reading and Writing Files · practice
Make File Failures Actionable
read_books works when its agreement is satisfied: the path exists, names a readable file, and contains UTF-8 text. Outside-world boundaries sometimes break that agreement.
Two failures are expected enough to help with here:
FileNotFoundError: nothing exists at the path Python tried to open;UnicodeDecodeError: bytes exist, but they are not valid UTF-8 text.
Neither means read_books should return an empty
Keep the reusable function honest
Let read_books(path) raise these
Catch the failures in main(), where the program finally has enough context to tell a person what to do:
try:
books = read_books(BOOKS_PATH)
except FileNotFoundError:
print(f"Could not find {BOOKS_PATH}. Run this program from the project root.")
return
except UnicodeDecodeError:
print(f"Could not read {BOOKS_PATH} as UTF-8 text.")
return
After the two handlers, print Loaded 4 books. using the actual length. Later lessons will replace that temporary success message with durable output.
Catch only what you can repair
except Exception: would also catch a misspelled
Narrow handlers are not about making a program never fail. They are about improving failures whose meaning you understand while leaving unfamiliar failures visible.
The error text should include BOOKS_PATH. “File not found” names a category. “Could not find data/books.txt; run from the project root” gives a next move and connects back to the path rule from the first lesson.
Why should a missing file not become an empty list?
Implement the two narrow handlers and the temporary success message.
Then check that you did not overreach. Temporarily misspell a variable name inside read_books so it raises a NameError, and run the program. You want a traceback. If instead you see one of your two friendly file messages, the handler is catching more than it understands. Put the name back afterwards.
If you are unsure whether a handler is too wide, ask Monty: “What could except Exception catch here that I would rather see as a traceback?” The list is usually longer than people expect.
Task
Update main() so it:
calls
read_books(BOOKS_PATH);catches
FileNotFoundError, prints a message containingBOOKS_PATHand the project-root next step, then returns;catches
UnicodeDecodeError, prints a message containingBOOKS_PATHandUTF-8, then returns;otherwise prints
Loaded N books.using the reallength.
Do not catch Exception or other unrelated programming errors.