Exit Suppressing an Exception

class Suppressor:
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit ran")
        return True

class Logger:
    def __enter__(self):
        return self
    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exit ran")
        return False

with Suppressor():
    print("A")
    raise ValueError("boom")
print("past A")

with Logger():
    print("B")
    raise ValueError("boom")
print("past B")

What happens when this runs?

A

Neither block's __exit__ runs, because __exit__ only fires when a with block exits normally: an exception skips straight past it to the enclosing scope.

B

A, exit ran, past A, B, exit ran, then an uncaught ValueError crashes the program: past B never prints.

C

A, exit ran, past A, B, exit ran, past B: once __exit__ has run, execution always continues after the with block regardless of what it returns.

D

The first with block re-raises the ValueError right after printing exit ran, because returning True from __exit__ only suppresses exceptions caught by an except clause inside the with block, not the block's own body.