Context Managers with __enter__ and __exit__0%
Using contextlib

Context Managers with __enter__ and __exit__

Beginner12 min readUpdated: Jul 11, 2026
Study Materials

Context Managers with __enter__ and __exit__

Context managers are Python's standard mechanism for deterministic resource acquisition and release. Formalized in PEP 343, the with statement guarantees that critical cleanup logic—such as releasing database connection locks, closing file descriptors, rolling back failed database transactions, or restoring system environments—executes reliably, even in the event of unexpected runtime exceptions.


1. The Context Management Protocol

A class qualifies as a context manager by implementing two dunder methods:

  • __enter__(self): Prepares the runtime environment and optionally returns a resource bound to the as <variable> target.
  • __exit__(self, exc_type, exc_val, exc_tb): Tears down the resource. Receives exception details if an error occurred inside the with block.
Output
Execute: with ContextManager() as target:
manager = ContextManager()
target = manager.__enter__()
Execute with-block body
┌─────────────────────┴─────────────────────┐
▼ ▼
Block Completed Cleanly Exception Occurred
│ │
▼ ▼
manager.__exit__(None, None, None) manager.__exit__(exc_type, exc_val, tb)
│ │
│ Did __exit__ return True?
│ ┌───────────┴───────────┐
│ YES NO
│ │ │
│ ▼ ▼
│ Exception Suppressed Exception Re-raised
│ │ │
└───────────────────────────────┴───────────────────────┘
Continue script execution

2. Anatomy of the __exit__ Signature

The __exit__ method accepts four arguments:

  1. 1
    self: The context manager instance.
  2. 2
    exc_type: The exception class (e.g., ValueError) if an exception was raised inside the block; otherwise None.
  3. 3
    exc_val: The exception instance/message (e.g., ValueError("invalid")); otherwise None.
  4. 4
    exc_tb: The traceback object; otherwise None.

Exception Suppression Semantics

Critical Rule: If __exit__ returns True, Python swallows (suppresses) the exception, allowing program execution to resume normally after the with block. If __exit__ returns False, None, or anything falsy, Python re-raises the exception up the call stack.
Python
from typing import Optional, Type
from types import TracebackType
 
class SafeIgnorer:
"""A context manager that suppresses specified exception types."""
 
def __init__(self, *exceptions_to_ignore: Type[BaseException]) -> None:
self.exceptions_to_ignore = exceptions_to_ignore
 
def __enter__(self) -> "SafeIgnorer":
return self
 
def __exit__(
self,
exc_type: Optional[Type[BaseException]],
exc_val: Optional[BaseException],
exc_tb: Optional[TracebackType]
) -> bool:
if exc_type is not None and issubclass(exc_type, self.exceptions_to_ignore):
print(f"[LOG] Suppressed expected exception: {exc_val}")
return True # Suppress the exception
return False # Let all other exceptions propagate
 
# Testing suppression
with SafeIgnorer(ZeroDivisionError, FileNotFoundError):
result = 10 / 0
print("This line will not run.")
 
print("Execution safely resumed after handled ZeroDivisionError!")

3. The as Target Nuance

The variable following the as keyword is bound to the return value of __enter__(), which is not necessarily self.

Visual Architecture & Process Flow

How data and code flow step-by-step

Flowchart
Step 1
def __enter__
self
Step 2
str:

4. Production Example: Atomic Database Transaction

In relational databases or transactional file systems, all operations within a block must either commit completely or roll back entirely upon failure:

Visual Architecture & Process Flow

How data and code flow step-by-step

Flowchart
Step 1
def __init__
self
Step 2
None:

5. Compound Context Managers

Python supports multiple context managers within a single with statement separated by commas. They are entered from left to right and exited in reverse order (right to left):

Python
# Multiple context managers entered sequentially
with open("source.txt", "w") as src, open("dest.txt", "w") as dst:
src.write("Initial data")
dst.write("Copied data")

6. Summary Comparison

Aspect__enter____exit__
TriggerStart of with blockEnd of with block (normal or exceptional)
Argumentsselfself, exc_type, exc_val, exc_tb
Return Value RoleBound to variable after asTrue suppresses exception; False/None re-raises
Primary UseSetup, lock acquisition, initializationTeardown, lock release, resource cleanup

Multiple Choice Questions

1.

What four arguments are passed to the __exit__ method by the Python runtime when a with block exits? A. self, result, args, kwargs B. self, exc_type, exc_val, exc_tb C. self, status_code, message, stack D. self, start_time, end_time, duration

Answer: B
Explanation:Python passes self along with the exception type (exc_type), exception value (exc_val), and traceback object (exc_tb). If no exception occurred, all three are None.

2.

What must __exit__ return in order to suppress an exception raised within the with block? A. None B. False C. True D. The exception instance itself

Answer: C
Explanation:Returning True (or any truthy value) from __exit__ informs the Python interpreter that the exception has been handled and should be suppressed instead of propagating upward.

3.

In the statement with Resource() as target:, what value is bound to the variable target? A. Always the Resource() instance itself B. The boolean status of whether the context opened successfully C. The return value of Resource().__enter__() D. A tuple containing (Resource(), __exit__)

Answer: C
Explanation:The target variable following the as keyword receives whatever value is explicitly returned by the __enter__() method.

4.

When multiple context managers are combined in a single statement, e.g. with ContextA() as a, ContextB() as b:, in what order are their __exit__ methods invoked? A. ContextA.__exit__ first, then ContextB.__exit__ B. ContextB.__exit__ first, then ContextA.__exit__ (LIFO / reverse order) C. Simultaneously in parallel threads D. Only the last manager's __exit__ is called

Answer: B
Explanation:Python treats compound with statements as nested contexts: entering left-to-right (ContextA then ContextB), and exiting right-to-left in reverse LIFO order (ContextB then ContextA).

5.

If an unhandled exception occurs inside __enter__, what happens to __exit__? A. __exit__ is called immediately with the exception details. B. __exit__ is NOT called, because the context was never successfully entered. C. __exit__ is called with None, None, None. D. Python crashes with a segmentation fault.

Answer: B
Explanation:If __enter__ fails or raises an exception, the context was never established, so the corresponding __exit__ method is not invoked.

Next Lesson

Using contextlib

Continue learning with hands-on practice, examples, and exercises in the upcoming topic.

Related Lessons

Previous LessonNext Lesson
Class DecoratorsUsing contextlib

Practice Quiz

Test your understanding of this lesson with 5 questions. Each question has one correct answer.

PrevNext