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
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.