0Pricing
Python Academy · Lesson

__enter__ and __exit__ Protocol

Build context managers using the dunder protocol.

__enter__ and __exit__ Protocol is a free Python Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Python Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Context Manager Protocol

Any object that defines __enter__ and __exit__ is a context manager and can be used with with.

class Managed:
    def __enter__(self):
        print("entering")
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        print("exiting")
        return False

with Managed() as m:
    print("inside")

__enter__ Return Value

__enter__ is called at the start of the with block. Its return value is bound to the variable after as.

class Resource:
    def __enter__(self):
        self.value = 42
        return self  # bound to 'r'

    def __exit__(self, *args):
        self.value = None
        return False

with Resource() as r:
    print(r.value)  # 42

__exit__ Arguments

__exit__(exc_type, exc_val, exc_tb) receives exception info if an error occurred. If no exception, all three are None.

class Safe:
    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type is ValueError:
            print(f"Suppressed ValueError: {exc_val}")
            return True   # suppress the exception
        return False      # re-raise anything else

with Safe():
    raise ValueError("oops")  # suppressed

Suppressing Exceptions

Return True from __exit__ to suppress the exception. Return False (or nothing) to let it propagate.

class Ignore:
    def __enter__(self): return self
    def __exit__(self, *args): return True  # swallow all exceptions

with Ignore():
    1 / 0

print("continues here")

A Database Connection Example

A classic use case: acquire a connection on enter, commit/rollback and close on exit.

class DBConn:
    def __enter__(self):
        self.conn = connect_db()
        return self.conn

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            self.conn.rollback()
        else:
            self.conn.commit()
        self.conn.close()
        return False

A Timer Context Manager

Measure elapsed time by recording start in __enter__ and computing delta in __exit__.

import time

class Timer:
    def __enter__(self):
        self.start = time.perf_counter()
        return self

    def __exit__(self, *args):
        self.elapsed = time.perf_counter() - self.start
        print(f"Elapsed: {self.elapsed:.3f}s")
        return False

with Timer():
    sum(range(1_000_000))

Nesting Context Managers

Multiple context managers can be combined on a single with line.

with open("in.txt") as src, open("out.txt", "w") as dst:
    dst.write(src.read())

Reusable vs Single-Use Managers

Context managers whose __enter__ allocates a fresh resource each time are reusable. Those that exhaust an internal state (like a lock held only once) may not be.

class Reusable:
    def __enter__(self):
        return []
    def __exit__(self, *a):
        return False

for _ in range(3):
    with Reusable() as lst:
        lst.append(1)
        print(lst)  # [1] each time

__aenter__ and __aexit__ (Async)

For async context managers, define __aenter__ and __aexit__ coroutines and use async with.

class AsyncDB:
    async def __aenter__(self):
        self.conn = await connect()
        return self.conn

    async def __aexit__(self, *args):
        await self.conn.close()

# async with AsyncDB() as conn:
#     ...

Wrapping File Objects

Python file objects already implement the context manager protocol — never call f.close() manually.

with open("data.txt", "r") as f:
    data = f.read()
# file is closed here automatically
print(f.closed)  # True

Implementing a Lock

Thread locks are context managers: __enter__ acquires, __exit__ releases.

import threading

lock = threading.Lock()

def update_shared():
    with lock:
        # critical section
        pass

Quick Check

What should __exit__ return to suppress an exception?

Recap

Implement __enter__ and __exit__ to create context managers. Return the resource from __enter__, handle cleanup and exception suppression in __exit__.

Frequently asked questions

Is the “__enter__ and __exit__ Protocol” lesson free?

Yes — the full text of “__enter__ and __exit__ Protocol” is free to read here on the web, and the Python Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Python Academy course, upgrade to CoddyKit PRO.

What will I learn in “__enter__ and __exit__ Protocol”?

Build context managers using the dunder protocol. You practise Python Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start Python Academy?

No prior experience is required. Python Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “__enter__ and __exit__ Protocol” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this Python Academy lesson?

Yes. Every Python Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. The with Statement in Depth
  2. __enter__ and __exit__ Protocol
  3. contextlib: @contextmanager
  4. Practical Context Manager Patterns
← Back to Python Academy