who‑calls
Who calls my function? — A tiny static caller‑tree explorer for Python.
who‑calls scans a project’s source code with the built‑in ast module,
builds a directed call‑graph (caller → callee), and prints every
call‑path that can reach a target function. Output is an ASCII tree whose
nodes are clickable file‑and‑line references in most modern
terminals and IDEs.
├── main @ app/entry.py:28
│ └── orchestrate @ app/tasks/flow.py:71
│ └── do_work @ app/worker.py:42 <─ target
└── cli_entry @ app/cli.py:13
└── do_work @ app/worker.py:42 <─ target
✨ Features
- Static analysis only – no code execution, so it’s safe to run on untrusted repos or in CI.
- Clickable labels – format
func @ path.py:LINEso VS Code, PyCharm, Zed, etc. jump straight to the definition. - Regex‑based exclusion – skip vendored or generated code
(
.git,.venv,.cache,testsby default). - Detects
self.method()/cls.method()calls inside classes. - Clear error on ambiguous target names; accepts fully‑qualified
package.module:Class.methodstyle.
🚀 Installation (with UV)
uv pip install who‑calls
UV? uv is a super‑fast drop‑in replacement for pip + virtualenv. If you prefer stock tools, replace
uv pipwithpip.
Dev / editable install
uv venv .venv && source .venv/bin/activate
uv pip install -e .[dev]
🛠️ CLI usage
who‑calls <function> [OPTIONS]
Options:
--root DIR Project root (default: current directory)
--exclude REGEX Regex of paths to ignore (default: \\ .git | .venv | .cache | tests)
Examples:
# show who can reach do_work()
who‑calls do_work
# fully‑qualified target
who‑calls app.worker.do_work
# scan ./src and ignore build folder
who‑calls process_order --root src --exclude "build|dist"
If the short name matches multiple symbols you’ll get:
⚠ Ambiguous function name. Matches:
• app.utils.filters:create_filter
• app.api.v1.picking_filters.queries:create_filter
Please specify full path like <module>.<func> or <module>.<Class>.<method>.
🔍 How it works (high‑level)
-
Recursively collect
*.pyfiles below--root, apply--exclude. -
Parse each file with
ast.parse. -
Record every
FunctionDef/AsyncFunctionDefwith a fully‑qualified dotted name (pkg.mod.Class.func). -
Discover call edges:
foo()→ unique match by name.self.foo()/cls.foo()→ method on same class.obj.foo()→ falls back to unique global match.
-
Build a
networkx.DiGraph, reverse it, keep only ancestors that can reach the target, then pretty‑print.
📦 Project structure
who‑calls/
├── pyproject.toml # uv build backend
└── who_calls/
├── __init__.py # exposes who_calls.main
└── cli.py # core logic (ast + networkx)
🖋️ License
MIT © 2025 janbjorge
Metadata
Release files for who-calls 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| who_calls-0.2.0.tar.gz | 6.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| who_calls-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 11.8 kB
Release files / who_calls-0.2.0.tar.gz
| Download URL | who_calls-0.2.0.tar.gz |
|---|---|
| Size | 6.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
624f1c6fef8e199dad016d9a10dc2b574d97fe9e2c53a6000637869bd1429c38
|
|
BLAKE2b-256 checksum How to use checksums |
5b0615c521071c3e98d44890ac8cd6c65117ed07f2e0d3d4b9bb56917cd68e97
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.16
|
Release files / who_calls-0.2.0-py3-none-any.whl
| Download URL | who_calls-0.2.0-py3-none-any.whl |
|---|---|
| Size | 5.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
0c794cfe284d1300b9702b201d1d18260229ed97d6bd445bdcff07516c842ee6
|
|
BLAKE2b-256 checksum How to use checksums |
8f0048f3821136d0439d5a51441321aea60487f16756af6f858768f1f787ac60
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.6.16
|