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?
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)
 
    
    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'
