ast-grep
無料Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns
日本語の概要は準備中です。原文の説明を表示しています。
Use when building Python async HTTP services or clients with aiohttp - web server routing, middleware, WebSocket, SSE, streaming, client sessions, pytest-aiohttp testing, or troubleshooting SSL and timeout issues
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Asynchronous HTTP client/server framework for Python.
Choose aiohttp when:
Consider alternatives:
from aiohttp import web
async def health_check(request):
return web.json_response({"status": "ok"})
async def create_user(request):
data = await request.json()
# Validate with Pydantic or manual checks
return web.json_response({"id": 1, "username": data["username"]}, status=201)
app = web.Application()
app.router.add_get('/health', health_check)
app.router.add_post('/users', create_user)
if __name__ == '__main__':
web.run_app(app, host='127.0.0.1', port=8080)
Use app.on_startup and app.on_cleanup for resource lifecycle:
from aiohttp import web
import asyncpg
async def init_db(app):
"""Create database connection pool."""
app['db_pool'] = await asyncpg.create_pool(
host='localhost',
port=5432,
database='app_db'
)
async def close_db(app):
"""Close database connection pool."""
await app['db_pool'].close()
app = web.Application()
app.on_startup.append(init_db)
app.on_cleanup.append(close_db)
app.router.add_get('/users', list_users)
For cleaner startup/shutdown with context manager semantics:
from aiohttp import web
async def lifespan_ctx(app):
"""Lifespan context manager for resource management."""
# Startup
app['db_pool'] = await create_db_pool()
app['cache'] = await create_cache()
yield # App runs here
# Cleanup
await app['cache'].close()
await app['db_pool'].close()
app = web.Application()
app.router.add_get('/data', data_handler)
# Run with: web.run_app(app, lifespan=lifespan_ctx)
Middleware wraps request handling for cross-cutting concerns:
from aiohttp import web
import time
import logging
logger = logging.getLogger(__name__)
@web.middleware
async def timing_middleware(request, handler):
"""Track request duration."""
start = time.perf_counter()
try:
response = await handler(request)
duration = time.perf_counter() - start
logger.info(f"{request.method} {request.path} {response.status} ({duration:.3f}s)")
return response
except Exception as e:
duration = time.perf_counter() - start
logger.error(f"{request.method} {request.path} failed after {duration:.3f}s: {e}")
raise
@web.middleware
async def auth_middleware(request, handler):
"""Authentication middleware."""
public_paths = ['/health', '/public/']
if any(request.path.startswith(p) for p in public_paths):
return await handler(request)
auth_header = request.headers.get('Authorization')
if not auth_header or not await validate_token(auth_header):
return web.json_response(
{"error": "Unauthorized"},
status=401,
headers={'WWW-Authenticate': 'Bearer'}
)
# Attach user info to request
request['user'] = await decode_token(auth_header)
return await handler(request)
# Combine middleware (applied left-to-right)
app = web.Application(middlewares=[timing_middleware, auth_middleware])
| Response Type | Use Case | Example |
|---|---|---|
web.Response(text=...) | Plain text, HTML | web.Response(text="OK", content_type="text/html") |
web.json_response(...) | JSON bodies (auto-serializes) | web.json_response({"key": "value"}, status=201) |
web.StreamResponse() | Streaming large responses | See streaming section below |
web.FileResponse() | File downloads | web.FileResponse('data.zip') |
web.HTTPFound() | Redirects | web.HTTPFound('/new-location') |
| HTTP exception classes | Error responses | web.HTTPBadRequest(), web.HTTPNotFound() |
Manual status setting:
# Explicit status codes
return web.json_response({"error": "not found"}, status=404)
return web.Response(text="Created", status=201)
# HTTP exception classes (automatic status)
raise web.HTTPBadRequest(reason="Invalid input")
return web.HTTPUnauthorized(headers={'WWW-Authenticate': 'Bearer'})
For large files or real-time data:
from aiohttp import web
import asyncio
async def stream_data(request):
"""Server-Sent Events style streaming."""
response = web.StreamResponse(
status=200,
headers={
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
'Connection': 'keep-alive'
}
)
await response.prepare(request)
try:
for i in range(10):
data = f"data: {{'count': {i}}}\n\n"
await response.write(data.encode())
await response.drain()
await asyncio.sleep(1)
finally:
await response.write_eof()
return response
async def stream_file(request):
"""Stream large file in chunks."""
response = web.StreamResponse()
response.headers['Content-Type'] = 'application/octet-stream'
response.headers['Content-Length'] = str(file_size)
await response.prepare(request)
async with aiofiles.open('large_file.bin', 'rb') as f:
while chunk := await f.read(8192):
await response.write(chunk)
return response
import aiohttp
import asyncio
async def fetch_data():
async with aiohttp.ClientSession() as session:
async with session.get('https://api.example.com/data') as response:
response.raise_for_status()
return await response.json()
asyncio.run(fetch_data())
import aiohttp
# Default connector (often insufficient for production)
# session = aiohttp.ClientSession() # ❌ BAD: Uses defaults
# Production-ready connector
connector = aiohttp.TCPConnector(
limit=100, # Total connection pool size (default: 100)
limit_per_host=30, # Max connections per host (default: 30)
ttl_dns_cache=300, # DNS cache TTL in seconds
ssl=True, # Verify SSL certificates
enable_cleanup_closed=True, # Clean closed connections
)
timeout = aiohttp.ClientTimeout(
total=30, # Total request timeout (connect + transfer)
connect=5, # Connection establishment timeout
sock_connect=5, # Socket connection timeout
sock_read=10, # Read timeout (per read operation)
)
session = aiohttp.ClientSession(
connector=connector,
timeout=timeout,
headers={'User-Agent': 'my-app/1.0'}
)
# ✅ GOOD: Reuse session across requests
async def process_multiple_urls(urls):
timeout = aiohttp.ClientTimeout(total=30)
connector = aiohttp.TCPConnector(limit=100)
async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
tasks = [fetch_url(session, url) for url in urls]
results = await asyncio.gather(*tasks, return_exceptions=True)
return results
async def fetch_url(session, url):
async with session.get(url) as response:
response.raise_for_status()
return await response.json()
# ❌ BAD: Creating session per request (causes socket exhaustion)
async def bad_pattern(urls):
results = []
for url in urls:
async with aiohttp.ClientSession() as session: # New session each time
async with session.get(url) as response:
results.append(await response.json())
# Session closed immediately after each request
return results
Symptom: OSError: [Errno 24] Too many open files or connection timeouts under load
Cause: Creating ClientSession() inside request handlers or loops without reuse. Each session maintains its own connection pool and file descriptors.
Fix:
# ❌ ANTI-PATTERN: Session created per request
async def handler(request):
session = aiohttp.ClientSession() # New session every request
async with session.get(url) as resp:
return web.json_response(await resp.json())
# ✅ FIX: Session stored in app, reused across requests
async def init_app():
app = web.Application()
app['session'] = aiohttp.ClientSession(
connector=aiohttp.TCPConnector(limit=100)
)
return app
async def cleanup_app(app):
await app['session'].close()
app = await init_app()
app.on_cleanup.append(cleanup_app)
async def handler(request):
session = request.app['session']
async with session.get(url) as resp:
return web.json_response(await resp.json())
Symptom: Connections accumulate, eventually hitting limit in TCPConnector, new requests hang
Cause: Missing ClientTimeout means no total timeout. A slow or hung server can hold connections indefinitely.
Fix:
# ❌ ANTI-PATTERN: No timeout specified
session = aiohttp.ClientSession()
async with session.get('https://slow-api.com/data') as resp:
# If server hangs, this waits forever
data = await resp.json()
# ✅ FIX: Always set timeouts
timeout = aiohttp.ClientTimeout(
total=30, # Max total time for entire request
connect=5, # Max time to establish connection
sock_read=10 # Max time between read operations
)
session = aiohttp.ClientSession(timeout=timeout)
Symptom: Connection established but data never arrives; or DNS resolution hangs
Cause: Only setting total timeout isn't enough. sock_read and connect catch specific failure modes.
Fix:
# ❌ ANTI-PATTERN: Only total timeout
timeout = aiohttp.ClientTimeout(total=60)
# ✅ FIX: Granular timeouts
timeout = aiohttp.ClientTimeout(
total=60, # Overall request timeout
connect=5, # Fail fast if can't connect
sock_connect=5, # Socket connection timeout
sock_read=30 # Read timeout (prevents stuck on slow responses)
)
Symptom: RuntimeError: Cannot call nested app.handler() or RuntimeError: Session is closed
Cause: ClientSession is bound to the event loop it was created on. Reusing it after asyncio.run() restarts the loop.
Fix:
# ❌ ANTI-PATTERN: Global session
session = aiohttp.ClientSession() # Created at module load
async def main():
asyncio.run(fetch_data()) # New event loop
# session is bound to old loop!
# ✅ FIX: Create session within event loop context
async def main():
async with aiohttp.ClientSession() as session:
await fetch_data(session)
asyncio.run(main())
Symptom: Memory growth, connection pool depletion over time
Cause: Not using async with or not calling response.release() leaves connections in limbo.
Fix:
# ❌ ANTI-PATTERN: Not consuming response
async with session.get(url) as response:
# Forgot to read/release
pass
# Response may not be fully released
# ✅ FIX: Always consume or explicitly release
async with session.get(url) as response:
data = await response.read() # Consume fully
# OR
async with session.get(url) as response:
if response.status != 200:
response.release() # Explicit release for early exit
raise Exception(f"Unexpected status: {response.status}")
Plain ClientSession is fine when:
Tune TCPConnector when:
limit and limit_per_hostlimit, keep limit_per_host moderateenable_cleanup_closed=Truettl_dns_cache=300 or higher# High-throughput single API
connector = aiohttp.TCPConnector(
limit=500,
limit_per_host=100,
ttl_dns_cache=300
)
# Many different hosts (aggregator pattern)
connector = aiohttp.TCPConnector(
limit=1000,
limit_per_host=10, # Don't overwhelm any single host
ttl_dns_cache=600
)
Use httpx instead when:
Use sync HTTP client (requests) when:
See references/testing.md for pytest-aiohttp fixtures, test client usage, and lifespan testing patterns.
Load these reference files on demand for specific topics:
references/middleware.md: Global error handlers, logging, CORS, rate limiting, JWT/basic auth patterns (when implementing cross-cutting concerns)references/client.md: WebSocket client, streaming uploads, compression, keepalive tuning (when optimizing client performance)references/testing.md: Application signals, lifespan context, test client, pytest-aiohttp fixtures (when writing tests)references/troubleshooting.md: Connection refused, timeouts, SSL errors, memory leaks (when debugging issues)Official Documentation: https://docs.aiohttp.org/ GitHub Repository: https://github.com/aio-libs/aiohttp pytest-aiohttp: https://pytest-aiohttp.readthedocs.io/
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when doing structural code search and rewriting - ast-grep linting, refactoring, multi-language patterns
日本語の概要は準備中です。原文の説明を表示しています。
Use when building GBA games with the BPCore Lua engine - entity, sprite and tilemap functions, SRAM save and load, link cable multiplayer protocol, camera and scrolling, or optimization patterns
日本語の概要は準備中です。原文の説明を表示しています。
Use when running background tasks with Celery - worker and broker configuration (Redis, RabbitMQ), task routing by name vs queue, chains/groups/chords, retry patterns (autoretry_for, retry_backoff), acks_late semantics, failure detection, and monitoring with Flower
日本語の概要は準備中です。原文の説明を表示しています。
Use when building Django applications - security hardening, authentication and permissions, ORM optimization, PostgreSQL features, Django 6.0, migrations, testing, and ecosystem libraries
日本語の概要は準備中です。原文の説明を表示しています。
Use when customizing Django Admin - save_formset, get_search_results, formsets, queryset optimization, db_index, custom URLs
日本語の概要は準備中です。原文の説明を表示しています。
Use when implementing Django authentication - local accounts, OAuth, email verification, MFA, OIDC, django-organizations
日本語の概要は準備中です。原文の説明を表示しています。