callscape
Point it at a codebase, get a browsable static HTML call-graph map — functions, callers, class/struct definitions, all cross-linked. One output file, no server, no dependencies at view time. Built on tree-sitter, so each new language is one self-contained PR.
pip install callscape # or: pip install -e ".[dev]" from a checkout
callscape path/to/repo -o map.html
open map.html
callscape: 12 files · 55 definitions · 174 call edges (163 unresolved)
wrote map.html — open it in a browser
What you get
- Overview — stats, per-language file counts, most-connected symbols.
- Symbol pages — signature, source, callers, callees; every name is a link.
- Call-flow graph — callers → symbol → callees as a clickable SVG.
- Search — press
/, filter by symbol or file path. - Single file — the whole map is one self-contained
.html. Email it, attach it to a PR, drop it on any static host.
Languages today: Python, JavaScript, Go.
How it works
- Walk the repo, parse each supported file with its tree-sitter grammar.
- Each language adapter extracts definitions (functions, methods, classes, structs, interfaces) and call sites from the syntax tree.
- Calls are attributed to their smallest enclosing definition, then resolved by name across the whole repo.
- The index is embedded as JSON into a self-contained HTML viewer (vanilla JS, no CDN, no telemetry).
Honest limitations
Resolution is name-based, not type-based. obj.render() links to every
definition named render — dynamic dispatch is approximated, not solved.
Names matching more than 12 definitions are treated as too generic and listed
as unresolved instead of spraying edges. This is a map for orienting yourself
in an unfamiliar codebase, not a precision static analyzer.
Adding a language (the fun part)
One adapter = one PR. An adapter is a single module in callscape/languages/
(~100 lines) exposing four things:
NAME = "ruby"
EXTENSIONS = {".rb"}
def language() -> tree_sitter.Language:
return Language(tree_sitter_ruby.language())
def extract(root_node, source_bytes) -> tuple[list[RawDef], list[RawCall]]:
# walk the tree; return definitions (name, kind, byte span, line span,
# signature, parent) and call sites (bare callee name, byte, line)
...
Then:
- Add the grammar package (
tree-sitter-ruby) topyproject.tomldependencies. - List the module in
_ADAPTER_MODULESincallscape/languages/__init__.py. - Add a
test_ruby_defs_and_callscase intests/test_languages.py(see the existing Python/JS/Go cases — ~30 lines each).
Enclosure attribution, cross-file resolution, and the viewer are all
language-agnostic — the adapter never touches them. Use
callscape/languages/go.py as the template; it is the smallest.
CLI
callscape PATH [-o OUT.html] [--title TITLE] [--exclude GLOB]...
--exclude globs are relative to the root and repeatable. node_modules,
virtualenvs, vendor, build output, and hidden directories are skipped
automatically; files over 1.5 MB are ignored.
Development
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/pytest
.venv/bin/callscape . -o demo/self.html --title "callscape (self)" # dogfood
MIT license.
Metadata
Release files for callscape 0.3.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 | Provenance | Actions |
|---|---|---|---|---|
| callscape-0.3.0.tar.gz | 20.8 kB | With provenance | Details for callscape-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| callscape-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for callscape-0.3.0-py3-none-any.whl |
Total release size: 42.6 kB
Release files / callscape-0.3.0.tar.gz
| Download URL | callscape-0.3.0.tar.gz |
|---|---|
| Size | 20.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f21e93fc31185941c742d6c14705dc083429503156540ed72507631efab3a01a
|
|
BLAKE2b-256 checksum How to use checksums |
7bfa59dde6f6ad1ab401a54c02c24a0f698270d11c62e6ddf14319c346a044dd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency logRelease files / callscape-0.3.0-py3-none-any.whl
| Download URL | callscape-0.3.0-py3-none-any.whl |
|---|---|
| Size | 21.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f4800e213e80559686a3625b10cea1e5fe2fc335be677354335d3bed8cbcc3ba
|
|
BLAKE2b-256 checksum How to use checksums |
a427a9a396b8865ff5bb66f99a3e7a558ef6c810e2870fe1d41079bf6b01179a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency logSecurity for callscape 0.3.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.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for callscape-0.3.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Aug 9, 2026.
Transparency logA 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.