0
# Context Management
1
2
Async context managers and decorators for resource management, providing async versions of contextlib utilities with additional safety features. These tools ensure proper cleanup of async resources and enable context-aware programming patterns.
3
4
## Capabilities
5
6
### Context Manager Creation
7
8
Create context managers from generator functions and existing objects.
9
10
```python { .api }
11
def contextmanager(func):
12
"""
13
Decorator to create async context manager from generator function.
14
15
Parameters:
16
- func: Callable[..., AsyncGenerator[T, None]] - Generator function yielding once
17
18
Returns:
19
Callable[..., ContextDecorator[T]] - Function returning context manager
20
21
Usage:
22
@contextmanager
23
async def my_context():
24
# setup code
25
yield value
26
# cleanup code
27
"""
28
```
29
30
### Context Manager Base Classes
31
32
Base classes for creating reusable context managers.
33
34
```python { .api }
35
class ContextDecorator:
36
"""
37
Base class for context managers usable as decorators.
38
39
Inheriting from this class allows context managers to be used
40
as decorators on async functions.
41
"""
42
43
def _recreate_cm(self):
44
"""
45
Create a copy of the context manager for decorator usage.
46
47
Returns:
48
Self - Copy of the context manager
49
"""
50
51
def __call__(self, func):
52
"""
53
Use context manager as decorator.
54
55
Parameters:
56
- func: Async callable to decorate
57
58
Returns:
59
Decorated async callable
60
"""
61
```
62
63
### Resource Management
64
65
Context managers for automatic resource cleanup.
66
67
```python { .api }
68
class closing:
69
"""
70
Context manager that calls aclose() on the wrapped object when exiting.
71
"""
72
73
def __init__(self, thing):
74
"""
75
Parameters:
76
- thing: Object with aclose() method - Resource to manage
77
"""
78
79
async def __aenter__(self):
80
"""
81
Enter context and return the managed resource.
82
83
Returns:
84
The managed resource object
85
"""
86
87
async def __aexit__(self, exc_type, exc_val, exc_tb):
88
"""
89
Exit context and close the resource.
90
91
Parameters:
92
- exc_type: Exception type or None
93
- exc_val: Exception value or None
94
- exc_tb: Exception traceback or None
95
"""
96
97
class nullcontext:
98
"""
99
No-op async context manager.
100
"""
101
102
def __init__(self, enter_result=None):
103
"""
104
Parameters:
105
- enter_result: T, optional - Value to return from __aenter__
106
"""
107
108
async def __aenter__(self):
109
"""
110
Enter context and return enter_result.
111
112
Returns:
113
T - The enter_result value
114
"""
115
116
async def __aexit__(self, exc_type, exc_val, exc_tb):
117
"""
118
Exit context (no-op).
119
120
Parameters:
121
- exc_type: Exception type or None
122
- exc_val: Exception value or None
123
- exc_tb: Exception traceback or None
124
"""
125
```
126
127
### Context Stack Management
128
129
Manage multiple context managers as a single unit.
130
131
```python { .api }
132
class ExitStack:
133
"""
134
Context manager for managing multiple async context managers.
135
"""
136
137
def __init__(self):
138
"""Initialize empty exit stack."""
139
140
def pop_all(self):
141
"""
142
Remove all contexts from stack and return new stack with them.
143
144
Returns:
145
ExitStack - New stack containing all contexts
146
"""
147
148
def push(self, exit):
149
"""
150
Add context manager or exit callback to stack.
151
152
Parameters:
153
- exit: AsyncContextManager or exit callback - Context to manage
154
155
Returns:
156
The same context manager or callback
157
"""
158
159
def callback(self, callback, *args, **kwargs):
160
"""
161
Add callback function to be called on exit.
162
163
Parameters:
164
- callback: Callable - Function to call on exit
165
- *args: Arguments for callback
166
- **kwargs: Keyword arguments for callback
167
168
Returns:
169
Callable - The callback function
170
"""
171
172
async def enter_context(self, cm):
173
"""
174
Enter context manager and add to stack.
175
176
Parameters:
177
- cm: AsyncContextManager[T] - Context manager to enter
178
179
Returns:
180
T - Result from context manager's __aenter__
181
"""
182
183
async def aclose(self):
184
"""Close all contexts in reverse order."""
185
186
async def __aenter__(self):
187
"""
188
Enter the exit stack context.
189
190
Returns:
191
ExitStack - This exit stack instance
192
"""
193
194
async def __aexit__(self, exc_type, exc_val, exc_tb):
195
"""
196
Exit all managed contexts in reverse order.
197
198
Parameters:
199
- exc_type: Exception type or None
200
- exc_val: Exception value or None
201
- exc_tb: Exception traceback or None
202
203
Returns:
204
bool - True if exception was handled
205
"""
206
```
207
208
## Usage Examples
209
210
### Custom Context Managers
211
```python
212
from asyncstdlib import contextmanager
213
import asyncio
214
215
@contextmanager
216
async def database_transaction():
217
"""Context manager for database transactions."""
218
print("BEGIN TRANSACTION")
219
try:
220
yield "transaction_handle"
221
except Exception:
222
print("ROLLBACK")
223
raise
224
else:
225
print("COMMIT")
226
227
async def transaction_example():
228
async with database_transaction() as tx:
229
print(f"Using {tx}")
230
# Automatic commit on success
231
232
try:
233
async with database_transaction() as tx:
234
print(f"Using {tx}")
235
raise ValueError("Something went wrong")
236
except ValueError:
237
pass # Automatic rollback on exception
238
```
239
240
### Context Manager as Decorator
241
```python
242
from asyncstdlib import contextmanager
243
244
@contextmanager
245
async def timing_context():
246
"""Context manager that measures execution time."""
247
import time
248
start = time.time()
249
try:
250
yield
251
finally:
252
end = time.time()
253
print(f"Execution took {end - start:.2f} seconds")
254
255
# Use as context manager
256
async def context_usage():
257
async with timing_context():
258
await asyncio.sleep(1) # "Execution took 1.00 seconds"
259
260
# Use as decorator (if inheriting from ContextDecorator)
261
@timing_context()
262
async def timed_function():
263
await asyncio.sleep(0.5) # "Execution took 0.50 seconds"
264
```
265
266
### Resource Management
267
```python
268
from asyncstdlib import closing, nullcontext
269
import aiofiles
270
271
async def resource_example():
272
# Automatic cleanup with closing
273
async with closing(await aiofiles.open("file.txt")) as file:
274
content = await file.read()
275
# File automatically closed on exit
276
277
# Conditional context manager
278
use_file = True
279
async with (aiofiles.open("data.txt") if use_file else nullcontext("default")) as resource:
280
if use_file:
281
data = await resource.read()
282
else:
283
data = resource # "default"
284
```
285
286
### Managing Multiple Contexts
287
```python
288
from asyncstdlib import ExitStack
289
import aiofiles
290
import aiohttp
291
292
async def multi_context_example():
293
async with ExitStack() as stack:
294
# Add multiple context managers
295
file1 = await stack.enter_context(aiofiles.open("input.txt"))
296
file2 = await stack.enter_context(aiofiles.open("output.txt", "w"))
297
session = await stack.enter_context(aiohttp.ClientSession())
298
299
# Add cleanup callback
300
stack.callback(lambda: print("All resources cleaned up"))
301
302
# Use all resources
303
data = await file1.read()
304
async with session.get("https://api.example.com/data") as resp:
305
api_data = await resp.text()
306
307
await file2.write(f"Combined: {data} + {api_data}")
308
309
# All contexts automatically exited in reverse order
310
# Callback executed last
311
```
312
313
### Transferring Contexts
314
```python
315
async def context_transfer():
316
stack1 = ExitStack()
317
318
async with stack1:
319
file1 = await stack1.enter_context(aiofiles.open("temp.txt", "w"))
320
await file1.write("test data")
321
322
# Transfer contexts to another stack
323
stack2 = stack1.pop_all()
324
325
# file1 is still open, managed by stack2
326
async with stack2:
327
# file1 closed here
328
pass
329
```
330
331
### Error Handling in Context Managers
332
```python
333
@contextmanager
334
async def error_handling_context():
335
"""Context manager with custom error handling."""
336
print("Setting up resource")
337
try:
338
yield "resource"
339
except ValueError as e:
340
print(f"Handling ValueError: {e}")
341
# Return True to suppress the exception
342
return True
343
except Exception as e:
344
print(f"Unexpected error: {e}")
345
# Re-raise unexpected exceptions
346
raise
347
finally:
348
print("Cleaning up resource")
349
350
async def error_example():
351
# ValueError is suppressed
352
async with error_handling_context() as resource:
353
raise ValueError("This will be handled")
354
355
print("Execution continues") # This runs because exception was suppressed
356
```