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

# Once published to PyPI:
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
          # Once published to PyPI: uvx async-doctor …
          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 this Cloud VM (Linux x86_64, 4 cores, 15 GiB RAM), the litellm/ package subdirectory (3009 .py files, with venv) cold-scans in about 15 s at ~2.1 GiB peak RSS. Full home-assistant/core (~20k .py files) completes around ~38 s / ~5.8 GiB. Prefer a package subdirectory (async-doctor path/to/pkg) when memory is tight. Warm runs reuse the whole-scan cache when inputs are unchanged. See docs/benchmark.md.

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 once the repo is public:
npx skills add maolonglong/async-doctor

Works once the repo is public. 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.0

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.0
File Size Uploaded Provenance Actions
async_doctor-0.1.0.tar.gz 279.5 kB Without provenance Details for async_doctor-0.1.0.tar.gz

Built distributions (wheels)

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

Total release size: 79.1 MB

Release files / async_doctor-0.1.0.tar.gz

Download URL async_doctor-0.1.0.tar.gz
Size 279.5 kB
Tags Source
SHA-256 checksum
How to use checksums
ab007685c4b9bb0efe98b8dd2e8ee9f72cd6320e0777adeb8dfeae5d53cfac25
BLAKE2b-256 checksum
How to use checksums
56b754ad295cef8d95428490ee37113c4730aa80a468c658c207b5bafe7ee112
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.0-py3-none-win_amd64.whl

Download URL async_doctor-0.1.0-py3-none-win_amd64.whl
Size 11.5 MB
Tags Python 3 Windows x86-64
SHA-256 checksum
How to use checksums
efd7d9e3d1027ad2fcc706e59e2d5b25661cab26c79d94b9aaa0094876ffc05f
BLAKE2b-256 checksum
How to use checksums
45faec8a37f7ad404bbffaf38e32d7648c195df86cded5d5e7a64c9165a80c8a
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.0-py3-none-musllinux_1_2_x86_64.whl

Download URL async_doctor-0.1.0-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
a77c004e661ca01ec52eeaed2baaeec15776911a15d36bdc4ae1ec39f85e2698
BLAKE2b-256 checksum
How to use checksums
33bbc71da5e00bf5faa0a3a8949a74d82741edf1ce52e7d3b037797cf52fcbc9
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.0-py3-none-musllinux_1_2_aarch64.whl

Download URL async_doctor-0.1.0-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
1df178cd5dd0f3706d20fa238008acced8f86be608c463fd086893a3e9f6f049
BLAKE2b-256 checksum
How to use checksums
616f1c76d8b35130efcd32dcc070c9e241075b53c499b14c244b4e2e1ccc726b
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.0-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl

Download URL async_doctor-0.1.0-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
dc1f71db47bea2d6ee18d06ef44fca4ccbe3be043acba80f7204754b768af5a1
BLAKE2b-256 checksum
How to use checksums
efb7c903d1f0176055dd545036cfbec2415b43203f341b0db34c2381496d4f5a
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.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl

Download URL async_doctor-0.1.0-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
4d1cbf8cd234917e484779d0ae9b724236f0a44d90a01d8dfb926e4a4f1142ab
BLAKE2b-256 checksum
How to use checksums
53c53f1b4f2b239d789787e8ffdef6d72de891bc71bbfb68596fbf6bb6942688
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.0-py3-none-macosx_11_0_arm64.whl

Download URL async_doctor-0.1.0-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
31a382f7776ea72d5e1d64a955e6ee90359c7559d5c108591b0214820851c2d3
BLAKE2b-256 checksum
How to use checksums
8c5284009ba2fdb48fa236005b14967cde7f372781c5920cb99db351ecee4417
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.0-py3-none-macosx_10_12_x86_64.whl

Download URL async_doctor-0.1.0-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
8132c489eb1f33d43db3b119a60a7a08a16dfb3a6963763b5ed58037e30f2827
BLAKE2b-256 checksum
How to use checksums
955a6757a5800a4d76e9d51c45f310219900361d8083ca55f2f6e3c207e02dd5
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.0

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.0 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