1.3.4Python Intermediate

Context managers — with statement, `__enter__` - `__exit__`

1,704 words8 min readdifficulty · medium3 backlinks

WHAT is a context manager?

with open("data.txt") as f:   # f = open(...).__enter__()
    data = f.read()
# f.__exit__(...) runs here, closing the file — even if read() raised

HOW with actually works (derive it from scratch)

The statement

with EXPR as VAR:
    BODY

is exactly equivalent to this de-sugared form:

mgr      = EXPR
VAR      = mgr.__enter__()          # ← return value goes to `as VAR`
try:
    BODY
except BaseException:
    # __exit__ gets the live exception info
    if not mgr.__exit__(*sys.exc_info()):
        raise                       # re-raise unless __exit__ returned True
else:
    mgr.__exit__(None, None, None)  # no exception → all three args are None
Figure — Context managers — with statement, `__enter__`  -  `__exit__`

Building one from first principles


Common mistakes (Steel-manned)


Recall Feynman: explain to a 12-year-old

Imagine borrowing a library book. When you walk in (__enter__) you check the book out. When you leave (__exit__) you must return it — even if you trip and fall on the way out. The with block is the librarian standing at the door making sure you never leave with the book. You don't have to remember to return it; the rule does it for you.


Recall

What two dunder methods define a context manager?
__enter__ and __exit__
What is bound to the as variable in a with statement?
The return value of __enter__
What are the three arguments passed to __exit__?
exc_type, exc_value, traceback (None×3 if no exception)
What does __exit__ returning a truthy value do?
Suppresses (swallows) the exception that occurred in the block
Does __exit__ run when the with body raises an exception?
Yes — it always runs, like a finally block
In @contextmanager, what splits setup from cleanup?
A single yield; code before is __enter__, code after (in finally) is __exit__
Why put cleanup in a finally inside a @contextmanager?
So cleanup runs even if the with-block body raises an exception
What is with EXPR as VAR: de-sugared to?
mgr=EXPR; VAR=mgr.enter(); try BODY; exit runs in try/finally with exc info
contextlib helper that ignores given exception types?
contextlib.suppress(*exceptions)

Connections

  • Exception handling — try-except-finallywith is sugar over try/finally.
  • Decorators@contextmanager is a decorator that converts a generator into a manager.
  • Generators and yield — the single-yield trick behind @contextmanager.
  • File I/O — the canonical with open(...) use case.
  • Threading and lockswith lock: guarantees release.
  • Dunder methods__enter__/__exit__ are part of the protocol family.

Concept Map

requires

implements

implements

return value bound to

performs

desugars into

always runs

performs

returns

True

False or None

common uses

with statement

Context manager object

__enter__ method

__exit__ method

as VAR binding

try / finally desugar

Setup phase

Guaranteed cleanup

__exit__ return value

Exception suppressed

Files, locks, timers, DB conns

Hinglish (regional understanding)

Intuition Hinglish mein samjho

Dekho, context manager ka idea bahut simple hai: koi bhi cheez jo automatically "setup" aur "cleanup" handle kare. Jaise file kholi toh band bhi karni padti hai, lock liya toh release bhi karna padta hai. Agar tum manually karoge toh kabhi na kabhi bhool jaoge — aur file handle leak ya deadlock ho jayega. with statement Python ka promise hai ki cleanup hamesha chalega, chahe beech mein error aa jaye.

Andar do dunder methods kaam karte hain. __enter__ block start hone par chalta hai, aur jo woh return karta hai wahi as ke baad wale variable mein aata hai. __exit__ block khatam hone par chalta hai — chahe normally ya exception se. Sabse important baat: __exit__ ko teen cheezein milti hain (exc_type, exc_value, traceback). Agar koi error nahi hua toh teeno None hote hain. Aur agar __exit__ se True return karoge toh error "nigl" liya jaata hai (suppress), warna error aage propagate ho jaata hai.

Internally with bas try/finally ka sugar hai — yahi reason hai ki cleanup guaranteed chalti hai. Class wala tareeka thoda lamba hai, isliye @contextmanager decorator use karte hain: ek single yield likho — yield se pehle ka code __enter__ hai, baad ka (finally mein) __exit__. Exam aur real code dono mein yeh pattern bahut aata hai, toh "ENTER sets, EXIT resets" yaad rakhna.

Go deeper — visual, from zero

Test yourself — Python Intermediate

Connections