Expert guidance on Python's contextlib module and context manager patterns for resource management with the with statement.
Expert guidance on Python's `contextlib` module and context manager patterns for proper resource management using the `with` statement.
This skill helps you work with Python context managers and the `contextlib` utilities. It covers:
When the user asks about Python context managers or resource management, follow these steps:
1. **Identify the Use Case**
- Determine if they need to create a custom context manager or use an existing utility
- Check if synchronous or asynchronous context management is required
- Understand the resource being managed (files, connections, locks, etc.)
2. **Recommend the Appropriate Pattern**
**For simple resource management:**
- Use `@contextmanager` decorator with generator functions for synchronous code
- Use `@asynccontextmanager` for async operations
- Show the try-finally pattern for guaranteed cleanup
**For existing objects:**
- Use `closing()` for objects with a `close()` method
- Use `aclosing()` for async objects with `aclose()` method
- Use `nullcontext()` as a no-op placeholder for optional context managers
**For exception handling:**
- Use `suppress()` to silently ignore specific exceptions
- Explain when suppressing exceptions is appropriate (cleanup operations, optional files)
**For stream redirection:**
- Use `redirect_stdout()` or `redirect_stderr()` for capturing or redirecting output
- Warn about non-thread-safe global state changes
3. **Provide Implementation Guidance**
**Creating a context manager with @contextmanager:**
```python
from contextlib import contextmanager
@contextmanager
def managed_resource(*args, **kwds):
# Acquire resource
resource = acquire_resource(*args, **kwds)
try:
yield resource # Must yield exactly once
finally:
# Release resource - always executes
release_resource(resource)
```
**Async context manager:**
```python
from contextlib import asynccontextmanager
@asynccontextmanager
async def get_connection():
conn = await acquire_db_connection()
try:
yield conn
finally:
await release_db_connection(conn)
```
**Exception handling in context managers:**
- Explain that exceptions in the `with` block are reraised at the `yield` point
- Show try-except-finally pattern for handling or logging exceptions
- Emphasize that exceptions must be reraised unless intentionally suppressed
4. **Handle Class-Based Context Managers**
If decorator approach isn't suitable, show the protocol implementation:
```python
class ResourceManager:
def __enter__(self):
# Setup code
return self # or resource object
def __exit__(self, exc_type, exc_val, exc_tb):
# Cleanup code
return False # True suppresses exceptions
```
5. **Address Advanced Patterns**
**Reentrant context managers:**
- Can be entered multiple times from the same thread
- Example: `redirect_stdout`, `suppress`
**Reusable context managers:**
- Can be used in multiple `with` statements
- Must reset state between uses
**Context managers as decorators:**
- Context managers created with `@contextmanager` can decorate functions
- A new generator instance is created for each function call
6. **Provide Safety and Best Practice Guidance**
- Always use try-finally to guarantee cleanup, even if exceptions occur
- Yield exactly once in generator-based context managers
- Return `None` (or `False`) from `__exit__` unless intentionally suppressing exceptions
- Avoid context managers with global side effects in library code or threaded contexts
- Use `aclosing()` for async generators to ensure proper cleanup on early exit
- For exception groups (Python 3.12+), `suppress` removes matching exceptions from `BaseExceptionGroup`
7. **Show Practical Examples**
**File-like resource management:**
```python
from contextlib import closing
from urllib.request import urlopen
with closing(urlopen('https://example.com')) as page:
for line in page:
print(line)
```
**Optional context managers:**
```python
def process_file(file_or_path):
if isinstance(file_or_path, str):
cm = open(file_or_path)
else:
cm = nullcontext(file_or_path)
with cm as file:
# Process file
```
**Suppressing exceptions:**
```python
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('temporary_file.tmp')
```
8. **Common Pitfalls to Warn About**
- Yielding more than once in `@contextmanager` (causes RuntimeError)
- Forgetting to reraise exceptions after logging them
- Using non-reentrant context managers in nested contexts
- Thread-safety issues with global state changes (`redirect_stdout`, `chdir`)
- Forgetting `await` in async context managers
**User:** "How do I create a context manager for a database connection?"
**Response:**
```python
from contextlib import contextmanager
@contextmanager
def database_connection(db_url):
conn = connect_to_database(db_url)
try:
yield conn
finally:
conn.close() # Guaranteed cleanup
with database_connection('postgresql://...') as conn:
conn.execute('SELECT * FROM users')
```
**User:** "I want to temporarily suppress file not found errors"
**Response:**
```python
from contextlib import suppress
with suppress(FileNotFoundError):
os.remove('optional_cache_file.tmp')
```
Based on official Python documentation: https://docs.python.org/3/library/contextlib.html
Key classes and functions:
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/python-context-managers/raw