Skip to main content

async-doctor

Your agent blocks the event loop. This catches it.

Zero-config static analysis for Python: finds sync/blocking IO reachable from async def code, and reports evidence an agent can act on. Non-goals (no runtime probing, no Trio-only / gevent-eventlet modelling) live in ARCHITECTURE.md.

Install

uvx async-doctor .
pipx run async-doctor .
pip install async-doctor && async-doctor .

# From this repo (wheel):
maturin build --release
uvx --from ./target/wheels/async_doctor-*.whl async-doctor --version

# From source (Rust toolchain required):
cargo install --git https://github.com/maolonglong/async-doctor async_doctor

Activate the project venv (or set VIRTUAL_ENV) before scanning so third-party imports resolve. Without a project environment, results silently degrade — async-doctor reports the interpreter/venv it used and prints a short note.

Quick start

async-doctor .
async-doctor --score .
async-doctor --format json .
async-doctor --format markdown . >> "$GITHUB_STEP_SUMMARY"
async-doctor --format sarif . > async-doctor.sarif

async-doctor explain blocking-sleep
async-doctor why app.py:12 --path .
async-doctor install-skill --path .

# CI / agent loops (full analysis; filtered reporting):
async-doctor --changed .
async-doctor --changed=origin/main .
async-doctor --write-baseline baseline.json .
async-doctor --baseline baseline.json .

Every finding includes the full call chain (file:line per hop), a concrete fix hint, a stable rule id, a docsAnchor into docs/rules.md, and a runnable why command. No config file — flags and inline comments only.

time.sleep(1)  # async-doctor: ignore[blocking-sleep]: brief sync boundary in CLI wrapper

Unused ignores fail the run. Exit 0 = clean, 1 = findings (or unused suppressions), 2 = tool error. Incomplete analysis sets complete=false and does not use exit 2. Candidates and advice never affect the exit code.

By default, entries under tests/ / docs/ / examples/ / scripts/ / hidden dirs are skipped (--include-non-production to include). reason=unresolved candidates are hidden unless --show-unresolved.

What it detects

  • Sync/blocking IO reachable from async def (sleep, socket, file, subprocess, locks, smtplib/ftplib, zip/tar, glob/pathlib walks, input, futures waits) with prove-only conditions and entry labels
  • Site-packages follow down to stdlib sinks; third-party hops marked in JSON
  • Third-party rules: requests, sync httpx, redis, SQLAlchemy, psycopg, boto3 client/resource calls, sync OpenAI/Anthropic, and more — async twins never fire as findings
  • Higher-order / offload: asyncio.to_thread, executors, inferred wrappers
  • Advice (offload-has-async-twin, dangling-task): offload cuts that only reach sinks with a high-confidence same-package async twin installed in the scanned venv, and bare asyncio.create_task / ensure_future statements that discard the returned Task (non-blocking; SARIF note; text Advice section, capped unless --expand)
  • Errors: nested-event-loop (asyncio.run / … from async without offload; loop-state guards like if loop is None / early-return if loop is not None: return are dropped), offload-coroutine (offload target is async def), sync-with-async-cm (sync with entering an async-only context manager)

GitHub Actions (SARIF / code scanning)

Upload SARIF so findings appear in GitHub code scanning. async-doctor exits 1 when it finds issues — append || true (same pattern as GitHub’s ESLint SARIF example) so the upload step still runs:

name: async-doctor
on:
  push:
    branches: [main]
  pull_request:

jobs:
  scan:
    runs-on: ubuntu-latest
    permissions:
      # required for all workflows
      security-events: write
      # required for private repositories
      actions: read
      contents: read
    steps:
      - uses: actions/checkout@v7
      - uses: astral-sh/setup-uv@v10.3.3
      - name: Install project deps
        run: uv sync --frozen
      - name: Run async-doctor
        run: |
          source .venv/bin/activate
          uvx async-doctor --format sarif . > async-doctor.sarif || true
      - uses: github/codeql-action/upload-sarif@v4
        with:
          sarif_file: async-doctor.sarif

Large repositories / memory

On a 4-core Cloud VM (Linux x86_64), the litellm/ package subdirectory (3009 .py files, with venv) cold-scans in about 8.40 s at ~1.11 GiB peak RSS — see docs/benchmark.md for all corpora. Prefer a package subdirectory (async-doctor path/to/pkg) when memory is tight, and --max-findings N when the JSON payload is what matters (litellm shrinks from ~38 MB to <1 MB at N=50 while score/sinkSites stay full-report). Warm runs reuse the whole-scan cache when inputs are unchanged.

Benchmark

Public cold/warm numbers on pinned asyncio corpora: docs/benchmark.md. Reproduce with ./bench/run.sh.

Agent integrations

# Built-in zero-Node installer (writes .agents/skills/async-doctor/SKILL.md + .claude/skills symlink):
async-doctor install-skill --path .

# Or via Node skills CLI:
npx skills add maolonglong/async-doctor

For GitHub code scanning, see the SARIF workflow above.

License

MIT. Bundled typeshed stubs (via ty_vendored) are Apache-2.0; see THIRD_PARTY_NOTICES.md.

Metadata

Release files for async-doctor 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for async-doctor 0.1.1
File Size Uploaded Provenance Actions
async_doctor-0.1.1.tar.gz 291.2 kB Without provenance Details for async_doctor-0.1.1.tar.gz

Built distributions (wheels)

Table of built distributions (wheels) for async-doctor 0.1.1
File Actions
async_doctor-0.1.1-py3-none-win_amd64.whl Python 3 none Windows x86-64 Details for async_doctor-0.1.1-py3-none-win_amd64.whl
async_doctor-0.1.1-py3-none-musllinux_1_2_x86_64.whl Python 3 none Linux musl 1.2+ x86-64 Details for async_doctor-0.1.1-py3-none-musllinux_1_2_x86_64.whl
async_doctor-0.1.1-py3-none-musllinux_1_2_aarch64.whl Python 3 none Linux musl 1.2+ ARM64 Details for async_doctor-0.1.1-py3-none-musllinux_1_2_aarch64.whl
async_doctor-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl Python 3 none Linux glibc 2.17+ x86-64 Details for async_doctor-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
async_doctor-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl Python 3 none Linux glibc 2.17+ ARM64 Details for async_doctor-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
async_doctor-0.1.1-py3-none-macosx_11_0_arm64.whl Python 3 none macOS 11.0+ ARM64 Details for async_doctor-0.1.1-py3-none-macosx_11_0_arm64.whl
async_doctor-0.1.1-py3-none-macosx_10_12_x86_64.whl Python 3 none macOS 10.12+ x86-64 Details for async_doctor-0.1.1-py3-none-macosx_10_12_x86_64.whl

Total release size: 79.3 MB

Release files / async_doctor-0.1.1.tar.gz

Download URL async_doctor-0.1.1.tar.gz
Size 291.2 kB
Tags Source
SHA-256 checksum
How to use checksums
eec0f8ee4e333b0cc268f34b0f9f8d9f5d397619feb2c1162f9658b78db175ed
BLAKE2b-256 checksum
How to use checksums
4f6137f367a260c70e3f4ac1267993947baf6715c02daf5a8916d07fb7b10349
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-win_amd64.whl

Download URL async_doctor-0.1.1-py3-none-win_amd64.whl
Size 11.6 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
79d29170b20018db1cd6ef1d6ab8fb8af7667aa886f481e1bce6e2e2ff46bfe3
BLAKE2b-256 checksum
How to use checksums
deff6e430604e1d288bd30224efe12fec8c64cd6c3381bb61d5843c203b790af
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-musllinux_1_2_x86_64.whl

Download URL async_doctor-0.1.1-py3-none-musllinux_1_2_x86_64.whl
Size 11.8 MB
Tags Linux musl 1.2+ x86-64 Python 3
SHA-256 checksum
How to use checksums
26eca627faa079c4535186e28d53f2f96ca9bfced5502bc68b7d9aef822cb5de
BLAKE2b-256 checksum
How to use checksums
cd0740822c4026cfd1ee0eed573811d5452f4c3db0b08cda2b3bf75c4f076a86
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-musllinux_1_2_aarch64.whl

Download URL async_doctor-0.1.1-py3-none-musllinux_1_2_aarch64.whl
Size 10.9 MB
Tags Linux musl 1.2+ ARM64 Python 3
SHA-256 checksum
How to use checksums
26f20dbab0bfd2c1c5dc3769b8ef14df23dfd9db6033fd78834f77d68c850750
BLAKE2b-256 checksum
How to use checksums
77a9d33ac47b0adc9a62ab31af8de80265c5324873265578749744c6f53f958a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL async_doctor-0.1.1-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Size 11.6 MB
Tags Linux glibc 2.17+ x86-64 Python 3
SHA-256 checksum
How to use checksums
5b429072b193227324c7f3793112e0dc0610cb44794706c6ef217be4f43c1133
BLAKE2b-256 checksum
How to use checksums
de7fbd762cfcad8eb1c861803ff1ce105b48d76fe267fb8dfc37e3d0a47a6f29
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL async_doctor-0.1.1-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Size 10.9 MB
Tags Linux glibc 2.17+ ARM64 Python 3
SHA-256 checksum
How to use checksums
3574e1662f02981adac0fdbd75e27ed1c4f9f81fb4ee22f3d3756492d1fb5736
BLAKE2b-256 checksum
How to use checksums
832224f794aff0f99b5b911ae3a43e6d118e7f9f26c1b4bcd3a84ef084cab41c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-macosx_11_0_arm64.whl

Download URL async_doctor-0.1.1-py3-none-macosx_11_0_arm64.whl
Size 10.8 MB
Tags Python 3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
863c2d88446f19cb6ee55037b6dd9acd3404092a2e8e8c4f812b84a9b586bc4f
BLAKE2b-256 checksum
How to use checksums
70923917c9c78c4973b55f56753203c67434f99fcad2f0f966826605e7282c5a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / async_doctor-0.1.1-py3-none-macosx_10_12_x86_64.whl

Download URL async_doctor-0.1.1-py3-none-macosx_10_12_x86_64.whl
Size 11.4 MB
Tags Python 3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
8b517a5b0af3caf3b465a31b224b8fa05adbe3ec75b2d7a95b07f470ffc4785b
BLAKE2b-256 checksum
How to use checksums
ea00d027ba6b2617051807111abd47baf07b09805bd8dfe0f28858b6f758900c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for async-doctor 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.1 This release

8 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page