Python

Context Managers

햣둘 2025. 6. 25. 08:05

Contextlib - Utilities for "with" statement contexts

This module provides utilities for common tasks involoving the with statement.

 

Utilities

Functions and classes provided : 

class contextlib.AbstractContextManager

An abstract base class for classes that implement object._enter_() and object._exit_().

A default implementation for object._enter_() is provided which returns self while object._exit_() is an abstract method which by default returns None. (Added in version 3.6)

 

class contextlib.AbstractAsyncContextManager

An abstract base class for classes that implement object._aenter_() and object._aexit_().

A default implementation for object._aenter_() is provided which returns self while object._aexit_() is an abstract method which by default returns None. (Added in version 3.7)

 

@contextlib.contextmanager

This function is a decorator that can be used to define a factory function for "with" statement context managers, without needing to create a class or separate _enter_() and _exit_() methods.

While many objects natively support use in "with" statements, sometimes a resource needs to be managed that isn't a context manager in its own right, and doesn't implement a close() method for use with contextlib.closing.

 

An abstract example would be the following to ensure correct resource management:

from contextlib import contextmanager

@contextmanager
def managed_resource(*args, **kwds):
	# Code to acquire resource, e.g.:
    resource = acquire_resource(*args, **kwds)
    try:
    	yield resource
    finally:
    	# Code to release resource, e.g.:
        release_resource(resource)

 

The function can then be used like this:

with managed_resource(timeout=3600) as resource:
	# Resource is released at the end of this block,
    # even if code in the block raises an exception

 

The function being decorated must return a generator-iterator when called.

This iterator must yield exactly one value, which will be bound to the targets in the with statement's as clause, if any.

 

At the point where the generator yields, the block nested in the with statement is excuted. The generator is then resumed after the block is exited. if an unhandled exception occurs in the block, it is reraised inside the generator at the point where the yield occured. Thus, you can use a try-except-finally statement to trap the error (if any), or ensure that some cleanup takes place. If an exception is trapped merely in order to log it or to perform some action(rather than to suppress it entirely), the generator must reraise that exception. Otherwise the generator context manager will indicate to the with statement that the exception has been handled, and execution will resume with the statement immediately following the with statement.

 

contextmanager() uses ContextDecorator so the context managers it creates can be used as decorator as well as in with statements. When used as a decorator, a new generator instance is implicitly created on each function call (this allow the otherwise "one-shot" context managers created by contextmanager() to meet the requirement that context managers support multiple invocations in order to be used as decorators.)

 

@contextlib.asynccontextmanager

Similar to contextmanager(), but creates an asynchronous context manager.

 

This function is a decorator that can be used to define a factory function for async with statement asynchronous context managers, without needing to create a class or separate __aenter__() and __aexit__() methods. It must be applied to an asynchronous generator function.

 

A simple example:

from contextlib import asynccontextmanager

@asynccontextmanager
async def get_connection():
	conn = await acquire_db_connection()
    try:
    	yield conn
    finally:
    	await release_db_connection(conn)

async def get_all_users():
	async with get_connection() as conn:
    	return conn.query('SELECT ...')

 (Added in version 3.7)

 

Context managers defined with asynccontextmanager() can be used either as decorators or with async with statements: 

import time
from contextlib import asynccontextmanager

@asynccontextmanager
async def timeit():
	now = time.monotonic()
    try:
    	yield
    finally:
    	print(f'it took {time.monotonic() - now}s to run')
        
@timeit()
async def main():
	# ... async code ...

When used as a decorator, a new generator instance is implicitly created on each function call.

This allows the otherwise "one-shot" context managers cretaed by asynccontextmanager() to meet the requirement that context managers support multiple invocations in order to be used as decorators.

 

Changed in version 3.10: Async context managers created with asynccontextmanager() can be used as decorators.