with

try/finally in one line: the manager’s __enter__ runs before the block, its __exit__ runs after it — on success, on an exception, on return.

Errors & contextPython 3 (all)Live demo
with
with open(path) as f:
    data = f.read()
several managers
with A() as a, B() as b:
    body
parenthesized (3.10+)
with (
    A() as a,
    B() as b,
):
    body
what it does
x = mgr.__enter__()
try:
    body
finally:
    mgr.__exit__(...)
Use for
files, locks, database transactions, temporary settings — anything that must be closed or undone
Result
a statement; as binds what __enter__ returned (for open(), the file itself)
Pairs with
open(), contextlib.contextmanager, contextlib.suppress, try / finally
Watch out
__exit__ returning True swallows the exception; the as name outlives the block

Demo

Live evaluation
Two managers in one with. They are entered left to right and exited in reverse — also when the body raises.
Try:
Inputs
ninttry 0
Code
log = []
class Tag:
    def __init__(self, name):
        self.name = name
    def __enter__(self):
        log.append('enter ' + self.name)
        return self
    def __exit__(self, exc_type, exc, tb):
        log.append('exit ' + self.name)
try:
    with Tag('outer'), Tag('inner'):
        log.append(100 // 4)
except ZeroDivisionError:
    log.append('caught outside')
log
Result
['enter outer', 'enter inner', 25, 'exit inner', 'exit outer']

In the order tab, "exit inner" comes before "exit outer" in both cases, and before the outer except sees the error — cleanup happens first, handling after. In the __exit__ returns True tab, n = 0 skips "rest of body" but the program carries on: the ZeroDivisionError was swallowed. Any other exception (a NameError, say) gets False back and escapes. In the @contextmanager tab the try/finally around yield is what makes cleanup run on errors.

Syntax slots

NameTypeRequiredDescription
managerexpressionyesEvaluated once. Its type must define __enter__ and __exit__ (a context manager).
as namename / patternnoBound to the return value of __enter__() — not necessarily the manager itself. Still bound after the block.
bodyblockyesRuns between __enter__ and __exit__. However it ends, __exit__ is called.

Common patterns

Read a file
The file is closed when the block ends, even on an exception.
with open('config.toml', encoding='utf-8') as f:
    text = f.read()
Hold a lock
Acquired on entry, released on exit.
with lock:
    counter += 1
Ignore one specific error
contextlib.suppress replaces try / except / pass.
from contextlib import suppress
with suppress(FileNotFoundError):
    os.remove('cache.tmp')
Your own context manager
contextlib.contextmanager turns a generator into one; wrap yield in try/finally.
from contextlib import contextmanager

@contextmanager
def timer(label):
    start = time.perf_counter()
    try:
        yield
    finally:
        print(label, time.perf_counter() - start)

Examples

1. The file is closed afterwards
with open('notes.txt', 'w') as f: f.write('hi') print(f.closed)
Returns
True
2. Several managers in one with
with open('a.txt', 'w') as a, open('b.txt', 'w') as b: a.write('1') b.write('2') print(a.closed, b.closed)
Returns
True True
3. Parenthesized form (3.10+)
with ( open('a.txt', 'w') as a, open('b.txt', 'w') as b, ): pass print(a.closed and b.closed)
Returns
True
4. as gets what __enter__ returns
class Box: def __enter__(self): return 'from __enter__' def __exit__(self, *exc): return False with Box() as value: print(value)
Returns
from __enter__
5. __exit__ sees the exception
class Show: def __enter__(self): return self def __exit__(self, exc_type, exc, tb): print('exit got', exc_type.__name__, exc) with Show(): int('x')
Returns
exit got ValueError invalid literal for int() with base 10: 'x' ValueError: invalid literal for int() with base 10: 'x'
6. contextlib.suppress
from contextlib import suppress with suppress(KeyError): {}['missing'] print('not reached') print('carried on')
Returns
carried on
7. Not a context manager
with 42: pass
Returns
TypeError: 'int' object does not support the context manager protocol

Pitfalls

1. Using the file after the with block
The name f still exists, but the file behind it is closed. Do the work inside the block.
read after the block
with open('data.txt', 'w') as f:
    f.write('abc')
with open('data.txt') as f:
    pass
f.read()
ValueError: I/O operation on closed file.
read inside
with open('data.txt', 'w') as f:
    f.write('abc')
with open('data.txt') as f:
    text = f.read()
text
'abc'
2. __exit__ that returns True by accident
Any truthy return from __exit__ suppresses the exception. Return None (just fall off the end) unless swallowing errors is the point.
return True
class Timer:
    def __enter__(self):
        return self
    def __exit__(self, *exc):
        print('timer stopped')
        return True
with Timer():
    1 / 0
print('error vanished')
timer stopped error vanished
return nothing
class Timer:
    def __enter__(self):
        return self
    def __exit__(self, *exc):
        print('timer stopped')
with Timer():
    1 / 0
print('error vanished')
timer stopped ZeroDivisionError: division by zero
3. @contextmanager without try / finally
An exception in the with body is re-raised at the yield. Without try/finally, the cleanup after yield never runs.
bare yield
from contextlib import contextmanager
@contextmanager
def tag():
    print('open')
    yield
    print('close')
with tag():
    1 / 0
open ZeroDivisionError: division by zero
try / finally
from contextlib import contextmanager
@contextmanager
def tag():
    print('open')
    try:
        yield
    finally:
        print('close')
with tag():
    1 / 0
open close ZeroDivisionError: division by zero

When to use

Use it
  • Any resource that must be released: files, sockets, locks, DB connections and transactions
  • Temporarily changing state and restoring it (decimal.localcontext, unittest.mock.patch, os.chdir helpers)
  • Ignoring one specific exception (contextlib.suppress)
Reach for something else
  • Catching and handling an error → try / except
  • Objects without __enter__/__exit__ → wrap them with contextlib.closing() or write a small manager

Notes

CPython impl
with A() as a, B() as b: is exactly nested with statements — B is entered only if A entered successfully, and exits run innermost first
Scope
with does not create a scope: the as name, and anything assigned in the body, stays bound after the block
as
The as keyword is shared with import and except; see the import page for its other uses
async
async with calls __aenter__ / __aexit__ and is only allowed inside async def

FAQ

It opens the file, binds it to the as name, and closes it when the block ends — also if the block raises or returns. It replaces f = open(...); try: ...; finally: f.close().

History

3.1
Several context managers in one with statement.
3.10
Parentheses may group the managers across several lines.