Consider the following:
with open(path, mode) as f:
return [line for line in f if condition]
Will the file be closed properly, or does using return
somehow bypass the context manager?
Yes, it acts like the finally
block after a try
block, i.e. it always executes (unless the python process terminates in an unusual way of course).
It is also mentioned in one of the examples of PEP-343 which is the specification for the with
statement:
with locked(myLock):
# Code here executes with myLock held. The lock is
# guaranteed to be released when the block is left (even
# if via return or by an uncaught exception).
Something worth mentioning is however, that you cannot easily catch exceptions thrown by the open()
call without putting the whole with
block inside a try..except
block which is usually not what one wants.
Yes.
def example(path, mode):
with open(path, mode) as f:
return [line for line in f if condition]
..is pretty much equivalent to:
def example(path, mode):
f = open(path, mode)
try:
return [line for line in f if condition]
finally:
f.close()
More accurately, the __exit__
method in a context manager is always called when exiting the block (regardless of exceptions, returns etc). The file object's __exit__
method just calls f.close()
(e.g here in CPython)
finally
keywrod is: def test(): try: return True; finally: return False
.
Yes. More generally, the __exit__
method of a With Statement Context Manager will indeed be called in the event of a return
from inside the context. This can be tested with the following:
class MyResource:
def __enter__(self):
print('Entering context.')
return self
def __exit__(self, *exc):
print('EXITING context.')
def fun():
with MyResource():
print('Returning inside with-statement.')
return
print('Returning outside with-statement.')
fun()
The output is:
Entering context.
Returning inside with-statement.
EXITING context.
The output above confirms that __exit__
was called despite the early return
. As such, the context manager is not bypassed.
Yes, but there may be some side effect in other cases, because it may should do something (like flushing buffer) in __exit__
block
import gzip
import io
def test(data):
out = io.BytesIO()
with gzip.GzipFile(fileobj=out, mode="wb") as f:
f.write(data)
return out.getvalue()
def test1(data):
out = io.BytesIO()
with gzip.GzipFile(fileobj=out, mode="wb") as f:
f.write(data)
return out.getvalue()
print(test(b"test"), test1(b"test"))
# b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff' b'\x1f\x8b\x08\x00\x95\x1b\xb3[\x02\xff+I-.\x01\x00\x0c~\x7f\xd8\x04\x00\x00\x00'
test
, out.getvalue()
happens before __exit__
is executed, so the result is as expected.
Success story sharing
else
could be added towith
to solve thattry with except
problem. edit: added to languageProcess.terminate()
is one of the few (the only?) scenario that doesn't guarantee the call of afinally
statement: "Note that exit handlers and finally clauses, etc., will not be executed."os._exit
is sometimes used - it exits the Python process without calling cleanup handlers.with
block, does the guarantee hold for as long as the generator keeps yielding values? for as long as anything references it? I.e. do i need to usedel
or assign a different value to the variable which holds the generator object?ValueError: I/O operation on closed file.
.