Learn-skills.dev python-notebooks-async
Use when writing or reviewing asyncio code in Jupyter notebooks or '#%%' cell workflows — structuring event-loop ownership, orchestrating async tasks, or choosing compatibility strategies. Also use when hitting RuntimeError: This event loop is already running, asyncio.run() failures in cells, or tasks silently never completing.
install
source · Clone the upstream repo
git clone https://github.com/NeverSight/learn-skills.dev
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/NeverSight/learn-skills.dev "$T" && mkdir -p ~/.claude/skills && cp -r "$T/data/skills-md/ahgraber/skills/python-notebooks-async" ~/.claude/skills/neversight-learn-skills-dev-python-notebooks-async && rm -rf "$T"
manifest:
data/skills-md/ahgraber/skills/python-notebooks-async/SKILL.mdsource content
Python Notebooks Async
Overview
Notebook kernels own the event loop; async code must cooperate with that ownership rather than fight it. This skill covers orchestration patterns, top-level
await, and compatibility constraints for .ipynb and #%% workflows.
Treat these recommendations as preferred defaults. When project constraints require deviation, call out tradeoffs and compensating controls.
When to Use
raisesasyncio.run()
inside a notebook cell.RuntimeError- Event-loop conflicts when mixing async libraries in Jupyter.
- Porting async scripts into notebook workflows.
- Orchestrating concurrent tasks (
,gather
) in IPython kernels.TaskGroup - Deciding where to place reusable async logic across notebook/module boundaries.
When NOT to Use
- Pure script or service code with no notebook involvement — see
.python-concurrency-performance - Synchronous notebook workflows with no async needs.
- General asyncio API design outside notebook contexts — see
.python-runtime-operations
Quick Reference
- Treat notebook kernels as loop-owned environments; never create a competing loop.
- Use top-level
instead ofawait
in notebook cells.asyncio.run() - Orchestrate concurrent work with
orasyncio.gather()
.asyncio.TaskGroup - Keep reusable async logic in regular
modules, imported into notebooks..py - Use
only as a constrained compatibility fallback, not a default.nest_asyncio - Avoid fire-and-forget tasks — always
or collect results explicitly.await
Common Mistakes
- Calling
in a notebook cell. The kernel already runs a loop;asyncio.run()
tries to start a second one and raisesasyncio.run()
. UseRuntimeError
directly instead.await - Applying
globally by default. It patches the loop to allow reentrant calls but masks design problems and can hide subtle concurrency bugs. Reserve it for legacy compatibility.nest_asyncio - Defining async helpers inline in cells instead of modules.
Inline definitions are lost on kernel restart and cannot be tested outside the notebook.
Extract to
files..py - Ignoring returned tasks or coroutines.
Calling an async function without
silently produces a never-executed coroutine object, with no error until results are missing downstream.await - Mixing blocking I/O with async in the same cell.
Synchronous calls like
block the event loop, starving concurrent tasks. Userequests.get()
,aiohttp
, orhttpx
.asyncio.to_thread()
Scope Note
- Treat these recommendations as preferred defaults for common cases, not universal rules.
- If a default conflicts with project constraints or worsens the outcome, suggest a better-fit alternative and explain why it is better for this case.
- When deviating, call out tradeoffs and compensating controls (tests, observability, migration, rollback).
Invocation Notice
- Inform the user when this skill is being invoked by name:
.python-design-modularity
References
references/notebooks-async.md