К основному содержанию
правило

asyncio — правило для Cursor: как писать асинхронный Python

Правило для Cursor задаёт, как писать асинхронный код на Python: единая точка входа, задачи без утечек, никаких блокировок цикла событий.

Установить в Cursor · Вручную: положить файлы Все способы и платформы ↓
.cursor/rules/asyncio.mdc
Открыть на GitHub

Что делает asyncio

Правило подключается к Cursor и применяется ко всем файлам **/*.py. Внутри — набор требований к коду на asyncio с примерами «плохо» и «хорошо»: запускать приложение через asyncio.run(), а не вручную крутить цикл событий, хранить ссылку на объект задачи из asyncio.create_task(), иначе сборщик мусора может её забрать и исключение потеряется. Для параллельной работы нескольких корутин правило советует asyncio.gather(), а asyncio.wait() — когда нужен контроль над условием завершения.

Отдельный раздел — про блокировки. Тяжёлые вычисления и синхронный ввод-вывод нельзя выполнять прямо в корутине: цикл событий встанет, и всё остальное подождёт. Такие куски правило велит уводить в loop.run_in_executor() с ThreadPoolExecutor или ProcessPoolExecutor. В разработке рекомендуется включать debug=True у asyncio.run() — тогда Python предупредит о корутинах, которые никто не дождался.

Правило рассчитано на Python со стандартной библиотекой asyncio, без сторонних фреймворков. Если в проекте уже есть свой стиль работы с задачами, часть требований придётся переписать под него — это повод сверить правило с текущим кодом до включения.

Какие задачи решает

  • поймать корутину, которую забыли дождаться
  • разложить тяжёлые вычисления по исполнителям, чтобы не морозить цикл событий
  • запустить несколько запросов параллельно вместо последовательных ожиданий
  • сохранить ссылку на задачу и не потерять её исключение
  • выбрать между gather и wait для группы задач

Примеры запросов агенту

  • Проверь этот файл на правила asyncio: где цикл событий блокируется?
  • Перепиши запуск так, чтобы использовался asyncio.run() с debug=True
  • Разнеси эти три запроса через asyncio.gather()

Как установить asyncio

Вручную: положить файлы

.cursor/rules/asyncio.mdc

Во frontmatter: description, globs, alwaysApply

По шагам, с командами для каждой платформы: Cursor rules: файлы .mdc, четыре режима и замена .cursorrules

Вопросы о asyncio

Как установить правило asyncio в Cursor?

Создайте файл .cursor/rules/asyncio.mdc в проекте и вставьте в него содержимое правила. Cursor подхватит его для файлов по маске **/*.py.

Что делает правило asyncio?

Задаёт требования к асинхронному коду на Python: точка входа через asyncio.run(), явное управление задачами, вынос блокирующих операций в исполнители.

Нужны ли сторонние библиотеки?

Нет, правило опирается на стандартную библиотеку asyncio и модуль concurrent.futures.

Исходный текст

asyncio.mdc · CC0-1.0
---
description: Definitive guidelines for writing robust, performant, and maintainable asynchronous Python code using the asyncio standard library.
globs: **/*.py
---
# asyncio Best Practices

`asyncio` is Python's standard library for writing concurrent, I/O-bound code using `async`/`await`. This guide provides opinionated, actionable rules to ensure your `asyncio` applications are structured, performant, and easy to maintain.

## 1. Structured Concurrency: Entry Points and Task Management

Always use `asyncio.run()` as the single top-level entry point for your asynchronous application. Manage concurrent operations explicitly with `asyncio.create_task()`, ensuring all tasks are awaited or handled.

### 1.1. Top-Level Entry Point

Use `asyncio.run()` once to start your main coroutine. Enable debug mode during development for critical warnings about un-awaited coroutines and resource leaks.

❌ BAD: Manually managing event loops or calling `loop.run_until_complete()`.
```python
import asyncio

async def main():
    print("Hello")

# Don't do this in application code; it's low-level and error-prone.
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
loop.close()
```

✅ GOOD: Use `asyncio.run()` with `debug=True` for development.
```python
import asyncio

async def main():
    print("Hello from main!")
    await asyncio.sleep(0.1)
    print("Goodbye from main!")

if __name__ == "__main__":
    asyncio.run(main(), debug=True) # Always enable debug in dev!
```

### 1.2. Launching Concurrent Tasks

Use `asyncio.create_task()` to schedule coroutines to run concurrently. *Always* store a reference to the `Task` object to prevent it from being garbage collected prematurely, which can lead to silent failures and unhandled exceptions.

❌ BAD: Calling a coroutine without `await` or `create_task()`.
```python
import asyncio

async def fetch_data(url: str):
    print(f"Fetching {url}...")
    await asyncio.sleep(1) # Simulate network I/O
    print(f"Finished fetching {url}")
    return f"Data from {url}"

async def main():
    # This coroutine will never run!
    fetch_data("http://example.com/api/data")
    print("Main finished without waiting for data.")

if __name__ == "__main__":
    asyncio.run(main(), debug=True)
# Output: RuntimeWarning: coroutine 'fetch_data' was never awaited
# Main finished without waiting for data.
```

✅ GOOD: Explicitly create and await tasks.
```python
import asyncio
from typing import Awaitable

async def fetch_data(url: str) -> str:
    print(f"Fetching {url}...")
    await asyncio.sleep(1)
    print(f"Finished fetching {url}")
    return f"Data from {url}"

async def main() -> None:
    task: Awaitable[str] = asyncio.create_task(fetch_data("http://example.com/api/data"))
    print("Main continues while data is fetched...")
    result: str = await task # Await the task to get its result
    print(f"Received: {result}")

if __name__ == "__main__":
    asyncio.run(main(), debug=True)
```

### 1.3. Grouping Tasks

For running multiple tasks concurrently and waiting for all of them, use `asyncio.gather()`. For more fine-grained control over completion conditions (e.g., first to complete), use `asyncio.wait()`.

❌ BAD: Awaiting tasks sequentially when they can run in parallel.
```python
import asyncio
import time

async def process_item(item_id: int):
    print(f"Processing item {item_id}...")
    await asyncio.sleep(1)
    print(f"Item {item_id} processed.")
    return f"Result {item_id}"

async def main():
    start = time.monotonic()
    # This will take ~3 seconds total
    await process_item(1)
    await process_item(2)
    await process_item(3)
    print(f"Total time: {time.monotonic() - start:.2f}s")

if __name__ == "__main__":
    asyncio.run(main(), debug=True)
```

✅ GOOD: Use `asyncio.gather()` for concurrent execution.
```python
import asyncio
import time
from typing import List

async def process_item(item_id: int) -> str:
    print(f"Processing item {item_id}...")
    await asyncio.sleep(1)
    print(f"Item {item_id} processed.")
    return f"Result {item_id}"

async def main() -> None:
    start = time.monotonic()
    # This will take ~1 second total
    results: List[str] = await asyncio.gather(
        process_item(1),
        process_item(2),
        process_item(3)
    )
    print(f"All items processed. Results: {results}")
    print(f"Total time: {time.monotonic() - start:.2f}s")

if __name__ == "__main__":
    asyncio.run(main(), debug=True)
```

## 2. Avoiding Blocking Operations

The `asyncio` event loop must *never* be blocked. Any CPU-bound work or synchronous I/O operations must be offloaded to an executor.

### 2.1. CPU-Bound Work

Use `loop.run_in_executor()` with a `ThreadPoolExecutor` (for CPU-bound work in a separate thread) or `ProcessPoolExecutor` (for true parallelism, avoiding GIL) to prevent blocking the event loop.

❌ BAD: Performing heavy computation directly in a coroutine.
```python
import asyncio
import time

async def cpu_intensive_task():
    print("Starting CPU-intensive task...")
    # Simulate heavy computation - this blocks the event loop!
    _ = sum(i * i for i in range(10**7))
    print("Finished CPU-intensive task.")
    return "CPU result"

async def main():
    start = time.monotonic()
    task = asyncio.create_task(cpu_intensive_task())
    await asyncio.sleep(0.1) # This sleep will be delayed by the CPU task
    print("Main continued (but was blocked).")
    result = await task
    end = time.monotonic()
    print(f"Total time: {end - start:.2f}s, Result: {result}")

if __name__ == "__main__":
    asyncio.run(main(), debug=True)
# Output shows sleep was blocked, total time > 1s
```

✅ GOOD: Offload CPU-bound work to `run_in_executor`.
```python
import asyncio
import time
from concurrent.futures import ThreadPoolExecutor

def blocking_cpu_task() -> str:
    print("Starting blocking CPU task in executor...")
    _ = sum(i * i for i in range(10**7))
    print("Finished blocking CPU task in executor.")
    return "CPU result"

async def main() -> None:
    start = time.monotonic()
    # Use default ThreadPoolExecutor (None) or a custom one
    result_future = asyncio.get_running_loop().run_in_executor(
        None, blocking_cpu_task
    )
    await asyncio.sleep(0.1) # This sleep will run concurrently
    print("Main continued

© sanjeed5 · CC0-1.0 · sanjeed5/awesome-cursor-rules-mdc

Похожие правила IDE

Все правила IDE по теме «Языки программирования» →
asyncio-concurrency-patterns manutej Скилл по конкурентности в Python: event loop, корутины, задачи, семафоры и блокировки. Пригодится, когда пишешь асинхронный сервис или клиент. CX Codex +1 489установок async-python-patterns Seth Hobson Скилл помогает писать асинхронный Python-код с asyncio, async/await и конкурентными задачами. Пригодится для API и приложений с большим числом I/O-операций. CX Codex 18 055установок rust-async-patterns Seth Hobson Скилл помогает писать и разбирать асинхронный Rust на Tokio: задачи, каналы, потоки и обработку ошибок. Пригодится для сетевых сервисов и отладки. CX Codex 20 625установок m07-concurrency actionbook Помогает разбирать конкурентный и асинхронный Rust-код: выбрать потоки, async/await, каналы или синхронизацию. Нужен при ошибках Send/Sync, дедлоках и гонках. CX Codex +1 3 500установок csharp-concurrency-patterns Aaronontheweb Помогает агенту выбирать подходящие абстракции многопоточности в .NET: от async/await до Channels и Akka.NET. Пригодится при проектировании очередей и избавлении от блокировок. CX Codex +1 1 600установок rust-systems-programming manutej Скилл по системному Rust: владение, заимствование, потоки, async, unsafe и оптимизация. Пригодится, когда пишешь низкоуровневый код или переписываешь C/C++. CX Codex +1 485установок

Из того же набора69

azure Правило для Cursor с набором требований к коду и инфраструктуре в Azure: IaC, безопасность, экономия ресурсов. Пригодится, если проект живёт в Azure. ★ 3 579звёзд репо aws Задаёт агенту стандарты разработки на AWS: только CDK v2, переиспользование окружения Lambda, идемпотентность и минимальные права IAM. ★ 3 579звёзд репо aws-ecs Правило для Cursor с набором требований к контейнерам на AWS ECS: неизменяемые образы, секреты из Secrets Manager, логи в stdout. Пригодится, когда агент пишет Dockerfile и task definition. ★ 3 579звёзд репо aws-amplify Правило задаёт Cursor стиль работы с AWS Amplify Gen 2: отдельные файлы ресурсов, модульные функции, типизированные запросы к данным. Пригодится, если бэкенд на Amplify растёт и хочется держать его в порядке. ★ 3 579звёзд репо apollo-graphql Правило для Cursor задаёт порядок работы с Apollo GraphQL: сначала схема на SDL, потом резолверы. Пригодится, если в проекте JS или JSX. ★ 3 579звёзд репо ant-design Правило задаёт агенту стандарты работы с Ant Design v6.x: тему через токены, сетку, доступность компонентов. Пригодится, если проект на React и Ant Design. ★ 3 579звёзд репо android-sdk Правило задаёт Cursor стандарты разработки Android на Java: MVVM, Jetpack, Hilt, Room и навигация. Пригодится, когда агент пишет код для Android-проекта. ★ 3 579звёзд репо aiohttp Правило для Cursor задаёт, как писать асинхронный HTTP-клиент на aiohttp: одна сессия на приложение, таймауты, обработка ошибок. ★ 3 579звёзд репо

Спонсоры проекта

Компании, которые помогают держать аналитику открытой