Entering a Flaky Context Manager in Python
Here’s a little Python problem I encountered recently.
I want to open and read a file cautiously. If open()
raises an OSError
, for whatever reason, I want to do nothing. Otherwise, I want to print out all the lines in the file. If I open the file, I want to always close()
it - even if the printing of the lines raises an error. But if the printing does raise an error, even an OSError
, that should be raised.
What’s the most idiomatic way to do this?
If you have experienced similar problems before, you were likely thinking of using try
around with open()
like so:
try:
with open("file.txt") as fp:
for line in fp:
print(line)
except OSError:
pass
This works great, apart from for the last point: “if the printing does raise an error, even an OSError
, that should be raised”. Right now such an OSError
would be caught by the except OSError
.
Following my post on limiting try clauses, we want to limit the try
to only be around open()
. Effectively, we want something like this non-existent, poorly indented syntax:
try:
with open("foobar") as fp:
except OSError:
pass
else:
for line in fp:
print(line)
It would be neat if it works, but it doesn’t. So how can we wrap a try
around just our open
context manager?
Enter the standard library’s contextlib.ExitStack
. It’s a meta context manager, allowing you to enter zero or more context managers, and it will close them later for you.
To solve our problem, we need to want to use with ExitStack()
combined with enter_context
on our open()
call. We then use a standard try/except/else
to catch OSError
on the open()
but nowhere else. The full solution looks like:
from contextlib import ExitStack
with ExitStack() as stack:
try:
fp = stack.enter_context(open("file.txt", "r"))
except OSError:
pass
else:
for line in fp:
print(line)
Fin
Thanks to Tom Grainger for reminding me to use ExitStack()
here. I hope this helps you use context managers betterer,
Adam
Read my book Boost Your Git DX to Git better.
One summary email a week, no spam, I pinky promise.
Related posts:
- Tuples versus Lists in Python
- Limit Your Try Clauses in Python
- Simplify Your If Statements That Return Booleans
Tags: python