aiohttp — правило для Cursor: асинхронный HTTP-клиент
Правило для Cursor задаёт, как писать асинхронный HTTP-клиент на aiohttp: одна сессия на приложение, таймауты, обработка ошибок.
.cursor/rules/aiohttp.mdcЧто делает aiohttp
Правило подключается к Cursor и применяется к файлам **/*.py. Оно описывает пять привычек в работе с aiohttp: держать одну долгоживущую ClientSession вместо создания новой на каждый запрос, ставить aiohttp[speedups], задавать таймауты через ClientTimeout и включать raise_for_status=True, выносить запросы в типизированные async-функции, пользоваться встроенной аутентификацией вместо ручной сборки заголовка Authorization.
Внутри — примеры «плохо» и «хорошо» с кодом. Видно, чем плох вариант с сессией на каждый вызов: теряется пул соединений и keep-alive. Для таймаутов показан разбор на total, connect и sock_read, а ошибки ловятся по типам ClientResponseError, ClientError и asyncio.TimeoutError.
Установка speedups тянет aiodns и backports.zstd для Python младше 3.14. Если в проекте уже есть свой слой HTTP-клиента, правило будет спорить с ним — это повод проверить, кто из них задаёт сессию и таймауты.
Какие задачи решает
- переписать запросы на одну общую сессию вместо сессии на каждый вызов
- задать таймауты на подключение и чтение
- развести обработку HTTP-ошибок и сетевых сбоев по типам исключений
- вынести повторяющуюся логику запросов в типизированные хелперы
- заменить ручную сборку заголовка авторизации на встроенные средства aiohttp
Примеры запросов агенту
- Проверь мой файл с aiohttp и перепиши создание ClientSession по правилам
- Добавь таймауты и raise_for_status в этот асинхронный запрос
- Сделай из этих двух функций типизированные хелперы fetch_json и post_json
Как установить aiohttp
Вручную: положить файлы
.cursor/rules/aiohttp.mdcВо frontmatter: description, globs, alwaysApply
Вручную: положить файлы
.claude/rules/aiohttp.mdПоле paths во frontmatter ограничивает файлы: globs из источника → paths
AGENTS.md
Codex читает AGENTS.md: вставьте текст правила туда
Вручную: положить файлы
.github/instructions/aiohttp.instructions.mdПоле applyTo задаёт, к каким файлам применять: globs из источника → applyTo
Вручную: положить файлы
.devin/rules/aiohttp.mdВручную: положить файлы
.clinerules/aiohttp.mdПо шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules
Из того же набора69
Похожие
Рядом в теме «Бэкенд и API»
Все правила IDE по теме «Бэкенд и API» →Вопросы о aiohttp
Как установить правило aiohttp в Cursor?
Положите файл по пути .cursor/rules/aiohttp.mdc в проекте — Cursor подхватит его как правило.
Что делает это правило?
Задаёт стиль асинхронного HTTP-клиента на aiohttp: одна долгоживущая ClientSession, таймауты, обработка ошибок по типам, типизированные хелперы и встроенная аутентификация.
К каким файлам применяется правило?
К Python-файлам по маске **/*.py.
Исходный текст
aiohttp.mdc · CC0-1.0
---
description: This guide defines definitive best practices for using aiohttp, focusing on efficient, robust, and maintainable asynchronous HTTP client code.
globs: **/*.py
---
# `aiohttp` Best Practices
`aiohttp` is our go-to for high-performance asynchronous HTTP client operations. These rules ensure our `aiohttp` code is consistent, efficient, and resilient.
## 1. Manage `ClientSession` Lifecycle Properly
Always use a single, long-lived `ClientSession` per application or logical component. This leverages connection pooling and keep-alive, drastically reducing overhead. Always use it as an `async` context manager to ensure connections are closed cleanly.
❌ **BAD: Creating a new session for every request**
```python
import aiohttp
import asyncio
async def fetch_data(url: str) -> str:
async with aiohttp.ClientSession() as session: # Session created and closed per call
async with session.get(url) as response:
return await response.text()
async def main():
await fetch_data("http://example.com/api/v1/data")
await fetch_data("http://example.com/api/v1/status") # New session, new connection
```
✅ **GOOD: Single, long-lived `ClientSession`**
```python
import aiohttp
import asyncio
# Create the session once, typically at application startup
# or pass it down through dependencies.
_GLOBAL_HTTP_SESSION: aiohttp.ClientSession | None = None
async def get_http_session() -> aiohttp.ClientSession:
global _GLOBAL_HTTP_SESSION
if _GLOBAL_HTTP_SESSION is None or _GLOBAL_HTTP_SESSION.closed:
_GLOBAL_HTTP_SESSION = aiohttp.ClientSession()
return _GLOBAL_HTTP_SESSION
async def close_http_session():
global _GLOBAL_HTTP_SESSION
if _GLOBAL_HTTP_SESSION and not _GLOBAL_HTTP_SESSION.closed:
await _GLOBAL_HTTP_SESSION.close()
_GLOBAL_HTTP_SESSION = None
async def fetch_data(session: aiohttp.ClientSession, url: str) -> str:
async with session.get(url) as response:
response.raise_for_status() # Always raise for non-2xx statuses
return await response.text()
async def main():
session = await get_http_session()
try:
data = await fetch_data(session, "http://example.com/api/v1/data")
status = await fetch_data(session, "http://example.com/api/v1/status")
print(f"Data: {data[:20]}..., Status: {status[:20]}...")
finally:
await close_http_session() # Ensure session is closed on app shutdown
```
## 2. Install All Speedups
Always install `aiohttp` with its `[speedups]` extra. This includes `aiodns` for faster DNS resolution and `backports.zstd` (for Python < 3.14) for modern compression, significantly boosting performance.
```bash
pip install aiohttp[speedups]
```
## 3. Implement Robust Timeouts and Error Handling
Explicitly define timeouts using `aiohttp.ClientTimeout` and enable `raise_for_status=True` for automatic HTTP error detection. Wrap requests in `try/except aiohttp.ClientError` to handle network and HTTP-level issues gracefully.
❌ **BAD: No timeouts, generic exception handling**
```python
async def fetch_unreliable(session: aiohttp.ClientSession, url: str) -> str:
# This request could hang indefinitely
async with session.get(url) as response:
# 4xx/5xx responses won't raise an error, requiring manual check
return await response.text()
```
✅ **GOOD: Explicit timeouts, `raise_for_status`, and specific error handling**
```python
from aiohttp import ClientSession, ClientTimeout, ClientError, ClientResponseError
import asyncio
async def fetch_reliable(session: ClientSession, url: str) -> str:
timeout = ClientTimeout(total=5, connect=1, sock_read=3) # 5s total, 1s connect, 3s read
try:
async with session.get(url, timeout=timeout, raise_for_status=True) as response:
return await response.text()
except ClientResponseError as e:
print(f"HTTP error for {url}: {e.status} - {e.message}")
raise
except ClientError as e:
print(f"Network or client error for {url}: {e}")
raise
except asyncio.TimeoutError:
print(f"Request to {url} timed out.")
raise
```
## 4. Use Typed, Reusable Request Helpers
Encapsulate common request patterns in small, type-hinted `async` functions. This improves readability, maintainability, and enables better IDE support.
❌ **BAD: Repeated request logic and untyped functions**
```python
async def get_user_data(session, user_id):
resp = await session.get(f"http://api.example.com/users/{user_id}")
return await resp.json()
async def create_user(session, user_data):
resp = await session.post("http://api.example.com/users", json=user_data)
return await resp.json()
```
✅ **GOOD: Type-hinted, reusable functions with `json=` shortcut**
```python
from typing import Any, Dict
from aiohttp import ClientSession
async def fetch_json(session: ClientSession, url: str) -> Dict[str, Any]:
"""Fetches JSON from a URL, raises for status, and returns parsed data."""
async with session.get(url, raise_for_status=True) as response:
return await response.json()
async def post_json(session: ClientSession, url: str, payload: Dict[str, Any]) -> Dict[str, Any]:
"""Posts JSON payload to a URL, raises for status, and returns parsed response."""
async with session.post(url, json=payload, raise_for_status=True) as response:
return await response.json()
async def get_user_data(session: ClientSession, user_id: str) -> Dict[str, Any]:
return await fetch_json(session, f"http://api.example.com/users/{user_id}")
async def create_user(session: ClientSession, user_data: Dict[str, Any]) -> Dict[str, Any]:
return await post_json(session, "http://api.example.com/users", user_data)
```
## 5. Prefer Built-in Authentication and Session Headers
Use the `auth` parameter for basic authentication or `DigestAuthMiddleware` for digest auth. For other authentication schemes or default headers, set them when initializing `ClientSession` or update `session.headers`. Avoid manual `Authorization` header manipulation where built-in options exist.
❌ **BAD: Manually constructing `Authorization` headers**
```python
import base64
async def fetch_with_basic_auth(session: ClientSession, url: str, user: str, password: str):
auth_str = base64.b64encode(f"{user}:{password}".encode()).decode()
headers = {"Authorization": f"Basic {auth_str}"}
async with session.get(url, headers=headers) as response:
return await response.text()
```
✅ **GOOD: Using `auth` parameter for basic auth**
```python
from aiohttp import ClientSession, BasicAuth
async def fetch_with_basic_auth(session: ClientSession, url: str, user: str, password: str):
auth = BasicAuth(login=user, password=password)
async with session.get(url, auth=auth) as response:
response.raise_for_status()
return await response.text()
# For session-wide default headers (e.g., Bearer tokens)
async def create_auth_session(token: str) -> ClientSession:
headers = {"Authorization": f"Bearer {token}"}
return ClientSession(headers=headers)
```
## 6. Leverage Client Middleware for Cross-Cutting Concerns
For concerns like tracing, retries, or rate limiting that apply across many requests, implement client-side middleware. This keeps your request logic clean and separates concerns effectively.
```python
from aiohttp import ClientSession, ClientRequest, ClientResponse
from aiohttp.typedefs import ClientHandler
import logging
logging.basicConfig(level=logging.INFO)
async def logging_middleware(req: ClientRequest, handler: ClientHandler) -> ClientResponse:
"""Logs request and response details."""
logging.info(f"Requesting: {req.method} {req.url}")
response = await handler(req)
logging.info(f"Received: {response.status} for {req.method} {req.url}")
return response
async def main_with_middleware():
async with ClientSession(middlewares=(logging_middleware,)) as session:
await fetch_json(session, "http://httpbin.org/get")
await post_json(session, "http://httpbin.org/post", {"key": "value"})
# Example usage with the `fetch_json` and `post_json` helpers from above.
```
© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc