{"id":26022343,"url":"https://github.com/dimaqq/awaitwhat","last_synced_at":"2026-01-18T09:52:41.076Z","repository":{"id":48688570,"uuid":"201643039","full_name":"dimaqq/awaitwhat","owner":"dimaqq","description":"Await, What?","archived":false,"fork":false,"pushed_at":"2024-12-03T02:52:34.000Z","size":116,"stargazers_count":53,"open_issues_count":9,"forks_count":4,"subscribers_count":5,"default_branch":"dev","last_synced_at":"2025-12-28T12:56:47.041Z","etag":null,"topics":["async-await","asyncio","debug","python3"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dimaqq.png","metadata":{"files":{"readme":"readme.md","changelog":null,"contributing":null,"funding":null,"license":"license.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-08-10T14:45:45.000Z","updated_at":"2025-12-19T07:50:24.000Z","dependencies_parsed_at":"2022-08-31T18:10:57.110Z","dependency_job_id":null,"html_url":"https://github.com/dimaqq/awaitwhat","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/dimaqq/awaitwhat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimaqq%2Fawaitwhat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimaqq%2Fawaitwhat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimaqq%2Fawaitwhat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimaqq%2Fawaitwhat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dimaqq","download_url":"https://codeload.github.com/dimaqq/awaitwhat/tar.gz/refs/heads/dev","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dimaqq%2Fawaitwhat/sbom","scorecard":{"id":343201,"data":{"date":"2025-08-11","repo":{"name":"github.com/dimaqq/awaitwhat","commit":"66f6f1ca8a124391c8f43efa87e5bd8df6ba7091"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Code-Review","score":0,"reason":"Found 1/26 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: license.txt:0","Info: FSF or OSI recognized license: MIT License: license.txt:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 7 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'dev'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-18T06:26:42.794Z","repository_id":48688570,"created_at":"2025-08-18T06:26:42.794Z","updated_at":"2025-08-18T06:26:42.794Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28534175,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async-await","asyncio","debug","python3"],"created_at":"2025-03-06T09:55:00.333Z","updated_at":"2026-01-18T09:52:41.055Z","avatar_url":"https://github.com/dimaqq.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"# Await, What?\n\nTells you what waits for what in an `async/await` program.\n\n### Build\n\n`uv build`\n\n### ⚠️ Python 3.10.0a1\n\nIt seems the API was changed in 3.10 and the C extension doesn't compile.\nI'll investigate...\n\n### Alpine\n\nYou'll need `apk add build-base openssl-dev libffi-dev`\n\n## 2019 Sprint Setup\n\nThis is out of date, kept for historical reasons.\n\nComms: https://gitter.im/awaitwhat/community (no longer in use)\n\n* Python 3.9, Python 3.8 (preferred) or Python 3.7\n* Your platform dev tools (compiler, etc).\n* Ensure that `python` is 3.9 or 3.8 or 3.7\n* Install `poetry`\n* Install `graphviz`\n* Clone this repository\n* Look at [tests](https://github.com/dimaqq/awaitwhat/tree/master/test)\n* Look at [issues](https://github.com/dimaqq/awaitwhat/issues)\n\n```console\n\u003e python --version\nPython 3.9.0b4  #🧡\nPython 3.8.4    #👌\n\n\u003e dot -V\ndot - graphviz version 2.40.1\n\n\u003e curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python\n# add ~/.poetry/bin to your PATH\n\n\u003e git clone git@github.com:dimaqq/awaitwhat.git\n\u003e cd awaitwhat\n~/awaitwhat (dev|✔) \u003e poetry shell    # creates a venv and drops you in it\n\n(awaitwhat-x-py3.9) ~/awaitwhat (dev|✔) \u003e poetry install  # installs projects dependencies in a venv\n(awaitwhat-x-py3.9) ~/awaitwhat (dev|✔) \u003e poetry build    # builds a C extension in this project\n\n(awaitwhat-x-py3.9) ~/awaitwhat (dev|✔) \u003e env PYTHONPATH=. python examples/test_shield.py | tee graph.dot\n(awaitwhat-x-py3.9) ~/awaitwhat (dev|✔) \u003e dot -Tsvg graph.dot -o graph.svg\n(awaitwhat-x-py3.9) ~/awaitwhat (dev|✔) \u003e open graph.svg  # or load it in a browser\n```\n\n### TL;DR\n\nSay you have this code:\n```py\n\nasync def job():\n    await foo()\n\n\nasync def foo():\n    await bar()\n\n\nasync def bar():\n    await baz()\n\n\nasync def baz():\n    await leaf()\n\n\nasync def leaf():\n    await asyncio.sleep(1)  # imagine you don't know this\n\n\nasync def work():\n    await asyncio.gather(..., job())\n```\n\nNow that code is stuck and and you want to know why.\n\n#### Python built-in\n```py\nStack for \u003cTask pending coro=\u003cjob() …\u003e wait_for=\u003cFuture pending cb=[\u003cTaskWakeupMethWrapper …\u003e()]\u003e cb=[…]\u003e (most recent call last):\n  File \"test/test_stack.py\", line 34, in job\n    await foo()\n```\n\n#### This library\n```py\nStack for \u003cTask pending coro=\u003cjob() …\u003e wait_for=\u003cFuture pending cb=[\u003cTaskWakeupMethWrapper …\u003e()]\u003e cb=[…]\u003e (most recent call last):\n  File \"test/test_stack.py\", line 34, in job\n    await foo()\n  File \"test/test_stack.py\", line 38, in foo\n    await bar()\n  File \"test/test_stack.py\", line 42, in bar\n    await baz()\n  File \"test/test_stack.py\", line 46, in baz\n    await leaf()\n  File \"test/test_stack.py\", line 50, in leaf\n    await asyncio.sleep(1)\n  File \"/…/asyncio/tasks.py\", line 568, in sleep\n    return await future\n  File \"\u003cSentinel\u003e\", line 0, in \u003c_asyncio.FutureIter object at 0x7fb6981690d8\u003e: …\n```\n\n### Dependency Graph\n\n\u003cimg src=\"https://raw.github.com/dimaqq/awaitwhat/master/doc/test_future.svg?sanitize=true\"\u003e\n\n### References\n\nhttps://mail.python.org/archives/list/async-sig@python.org/thread/6E2LRVLKYSMGEAZ7OYOYR3PMZUUYSS3K/\n\n\u003e Hi group,\n\u003e\n\u003e I'm recently debugging a long-running asyncio program that appears to get stuck about once a week.\n\u003e\n\u003e The tools I've discovered so far are:\n\u003e * high level: `asyncio.all_tasks()` + `asyncio.Task.get_stack()`\n\u003e * low level: `loop._selector._fd_to_key`\n\u003e\n\u003e What's missing is the middle level, i.e. stack-like linkage of what is waiting for what. For a practical example, consider:\n\u003e\n\u003e ```py\n\u003e async def leaf(): await somesocket.recv()\n\u003e async def baz(): await leaf()\n\u003e async def bar(): await baz()\n\u003e async def foo(): await bar()\n\u003e async def job(): await foo()\n\u003e async def work(): await asyncio.gather(..., job())\n\u003e async def main(): asyncio.run(work())\n\u003e ```\n\u003e\n\u003e The task stack will contain:\n\u003e * main and body of work with line number\n\u003e * job task with line number pointing to foo\n\u003e\n\u003e The file descriptor mapping, socket fd, `loop._recv()` and a `Future`.\n\u003e\n\u003e What's missing are connections `foo-\u003ebar-\u003ebaz-\u003eleaf`.\n\u003e That is, I can't tell which task is waiting for what terminal `Future`.\n\u003e\n\u003e Is this problem solved in some way that I'm not aware of?\n\u003e Is there a library or external tool for this already?\n\u003e\n\u003e Perhaps, if I could get a list of all pending coroutines, I could figure out what's wrong.\n\u003e\n\u003e If no such API exists, I'm thinking of the following:\n\u003e\n\u003e ```py\n\u003e async def foo():\n\u003e     await bar()\n\u003e\n\u003e In [37]: dis.dis(foo)\n\u003e   1           0 LOAD_GLOBAL              0 (bar)\n\u003e               2 CALL_FUNCTION            0\n\u003e               4 GET_AWAITABLE\n\u003e               6 LOAD_CONST               0 (None)\n\u003e               8 YIELD_FROM\n\u003e              10 POP_TOP\n\u003e              12 LOAD_CONST               0 (None)\n\u003e              14 RETURN_VALUE\n\u003e ```\n\u003e\n\u003e Starting from a pending task, I'd get it's coroutine and:\n\u003e\n\u003e Get the coroutine frame, and if current instruction is `YIELD_FROM`, then the reference to the awaitable should be on the top of the stack.\n\u003e If that reference points to a pending coroutine, I'd add that to the \"forward trace\" and repeat.\n\u003e\n\u003e At some point I'd reach an awaitable that's not a pending coroutine, which may be: another `Task` (I already got those), a low-level `Future` (can be looked up in event loop), an `Event` (tough luck, shoulda logged all `Event`'s on creation) or a dozen other corner cases.\n\u003e\n\u003e What do y'all think of this approach?\n\u003e\n\u003e Thanks,\n\u003e D.\n\n### Build for Alpine\n\nThis is out of date...\n\n```sh\ncd /src\napk update\napk add build-base openssl-dev libffi-dev curl\ncurl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python\ncp -a ~/.poetry/lib/poetry/_vendor/py3.8 ~/.poetry/lib/poetry/_vendor/py3.9\nsource $HOME/.poetry/env\npoetry install\npoetry run pytest\nenv _PYTHON_HOST_PLATFORM=alpine_x86_64 poetry build\n```\n\n### Manylinux2014\n\nThis is out of date...\n\n🚧 Work in progress, doesn't tag the wheels correctly 🚧\n\n`docker run -v (pwd):/src -it quay.io/pypa/manylinux2014_x86_64 sh`\n\n```sh\ncurl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | /opt/python/cp38-cp38/bin/python\nsource $HOME/.poetry/env\ncd /src\npoetry env use /opt/python/cp38-cp38/bin/python\npoetry install\npoetry run pytest\npoetry build\npoetry env use /opt/python/cp39-cp39/bin/python\npoetry install\npoetry run pytest\npoetry build\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimaqq%2Fawaitwhat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdimaqq%2Fawaitwhat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdimaqq%2Fawaitwhat/lists"}