cgg — Python bindings
Offline, deterministic call graphs for 44 languages, in-process.
import cgg
g = cgg.analyze("./src")
print(g.to_mermaid())
No network calls, no language servers, no build artifacts required. The
analysis is the same Rust pipeline the cgg command-line tool runs, in
the same order, so the two cannot disagree — there is a parity test that
compares this module's JSON output against the binary's on the same tree.
Install
pip install cgg-callgraphgenerator
import cgg
The distribution is cgg-callgraphgenerator; the import is cgg.
PyPI's cgg belongs to an unrelated GGUF tool, so the short name was not
available. Python separates these two names routinely — pip install pillow gives you import PIL.
One caveat, because the other package also installs a top-level
cggmodule: do not install both into the same environment. Both write tosite-packages/cgg/, pip will not stop you, and whichever lands second overwrites the first. If you already havepip install cgg(the GGUF tool), use a separate virtualenv.
The extension is built against the stable ABI (abi3-py39), so a single
wheel per platform covers every CPython ≥ 3.9 — no per-version builds.
Five prebuilt wheels, ~10 MB each, so pip install needs no compiler
on any of them:
| Wheel | Covers |
|---|---|
manylinux_2_17_x86_64 |
x86-64 Linux (glibc ≥ 2.17) |
manylinux_2_28_aarch64 |
arm64 Linux (glibc ≥ 2.28) |
macosx_10_12_x86_64 |
Intel macOS |
macosx_11_0_arm64 |
Apple-silicon macOS |
win_amd64 |
x86-64 Windows |
An sdist ships too, so pip install still succeeds off that list — musl
Linux (Alpine) and Windows on arm64 are the ones that reach for it. There
pip builds from source, which needs a Rust toolchain (≥ 1.85) and takes a
few minutes.
Usage
import cgg
# Whole tree.
g = cgg.analyze("./src")
# A neighbourhood around what you care about.
g = cgg.analyze("./src", filter=[r"handle_request$"], hops=2)
# Several trees, one graph.
g = cgg.analyze(["./api", "./worker"], lang=["python", "go"])
g.to_mermaid() # str — what agents read; byte-identical to `cgg -t mermaid`
g.to_json() # str — `cgg -t json`, bar the per-run timings it embeds
g.to_dot() # str — Graphviz
g.to_graphml() # str — Gephi / yEd / networkx
g.to_dict() # dict — the escape hatch
len(g) # callable count
g.callables # tuple[Callable, ...]
g.edges # tuple[Edge, ...]
g.files # tuple[File, ...]
g.metrics # run counters
g.notices # what the CLI would print to stderr
g.jobs # worker threads the run actually used
g.callable("mypkg.mod.func") # Callable | None
g.callers_of("mypkg.mod.func") # list[Callable]
g.callees_of("mypkg.mod.func") # list[Callable]
Finding code nothing calls
g = cgg.analyze("./src", dead_code=True, dead_code_confidence="high")
paths = {f.id: f.path for f in g.files}
for c in g.callables:
if c.unreferenced:
print(f"{c.unreferenced:6} {c.qualified_name} {paths[c.file]}:{c.start_line}")
BEST EFFORT. Every finding is a hypothesis. It means cgg could not find a caller, not that none exists — reflection, FFI, a framework cgg has no rules for, and dynamic dispatch all produce callers it cannot see.
Filtering by trust
Every edge carries how it was established and how much cgg trusts it, so you can narrow to what you are willing to rely on:
solid = [e for e in g.edges if e.confidence == "high" and e.via == "direct"]
Two things worth knowing
Renderers never build Python objects. to_mermaid() and friends
render straight from the Rust graph; g.callables constructs one Python
object per callable, once, then caches. Measured on cgg's own crates/
(2,019 callables): to_mermaid() produces 180 KB in 1.5 ms, the first
.callables access costs 0.84 ms, and every access after it costs 0.2 µs.
Both are small here and both scale with the graph, so on a repository an
order of magnitude larger the attribute path is what you would notice.
Reach for the renderer when a string is what you want.
Concurrent analyze() calls actually run concurrently. The GIL is
released (py.detach) and there is no internal lock, so a thread pool
scales. N analyses of crates/cgg-lang/src/plugins from a
ThreadPoolExecutor(N), against one analysis alone (56 ms) — medians of
four repetitions, 32-core host, jobs at its default of 8:
| threads | wall | vs. one analysis |
|---|---|---|
| 1 | 55 ms | 0.97x |
| 2 | 65 ms | 1.15x |
| 4 | 76 ms | 1.34x |
| 8 | 88 ms | 1.57x |
Four analyses for 1.34x the wall clock of one; eight for 1.57x. Absolute numbers are machine-specific and each analysis is already internally parallel — regenerate them rather than trusting them.
Earlier builds would have had to take a process-wide lock for the whole of
analyze, because extraction read two process-global switches
(DEADCODE_SIGNALS and EXTRA_REGISTRAR_VERBS) that a second concurrent
call would corrupt. Those now travel in a per-run cgg_lang::ExtractCtx,
so there is no lock and no shared cell. Raising jobs on one call still
works and is simpler if you only have one tree to analyze.
Not in this release
--why-live proofs, the --write-roots baseline and the audit event
stream are reachable from the Rust API but have no keyword and no Graph
attribute here. Use the CLI for those.
The framework-coverage table is not missing — it arrives rendered, as
one of the strings in g.notices, naming both what cgg recognised and
what it saw without rules. What is missing is a structured object; parse
the notice or use cgg --framework-coverage if you need fields.
Building from source
scripts/build-python.sh # from the repository root
Needs cargo (Rust ≥ 1.85), uv and git — the script checks for all
three up front and stops if one is missing.
cargo build compiles the .so, but only maturin can make it importable
— it writes the wheel metadata and puts the library where Python will find
it. That is all build-python.sh does, plus provisioning an interpreter,
since abi3-py39 rules out anything older than 3.9 and a system python3
often is.
The crate is an ordinary workspace member. It builds without a Python
interpreter present at all: abi3 fixes the ABI at compile time and
extension-module means libpython is never linked, so Py_* resolves at
load time from whichever interpreter imports the module.
License
Apache-2.0 OR MIT.
Release files for cgg-callgraphgenerator 0.7.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 | |
|---|---|---|---|
| cgg_callgraphgenerator-0.7.0.tar.gz | 606.9 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| cgg_callgraphgenerator-0.7.0-cp39-abi3-win_amd64.whl | CPython 3.9 | abi3 | Windows x86-64 | Details |
| cgg_callgraphgenerator-0.7.0-cp39-abi3-manylinux_2_28_aarch64.whl | CPython 3.9 | abi3 | Linux glibc 2.28+ ARM64 | Details |
| cgg_callgraphgenerator-0.7.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.9 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| cgg_callgraphgenerator-0.7.0-cp39-abi3-macosx_11_0_arm64.whl | CPython 3.9 | abi3 | macOS 11.0+ ARM64 | Details |
| cgg_callgraphgenerator-0.7.0-cp39-abi3-macosx_10_12_x86_64.whl | CPython 3.9 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 52.0 MB
Release files / cgg_callgraphgenerator-0.7.0.tar.gz
| Download URL | cgg_callgraphgenerator-0.7.0.tar.gz |
|---|---|
| Size | 606.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
36d70a63a033f86069d6eec23ae566ee1e03115b68056c72f239655cb1bd3e2f
|
|
BLAKE2b-256 checksum How to use checksums |
124b068b9898088cef4611f03fb3c5d062002a36b44515afcdc59053692baa98
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cgg_callgraphgenerator-0.7.0-cp39-abi3-win_amd64.whl
| Download URL | cgg_callgraphgenerator-0.7.0-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 10.2 MB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
8896a51554f47a126d2993f1426a67e0350114faddbc87972949ec0010f3bc64
|
|
BLAKE2b-256 checksum How to use checksums |
8c9b2a2c1ac0ed20e72d2ef5e54fbf2be0302e00b0abc89dc98bcf9e1a6c2fab
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cgg_callgraphgenerator-0.7.0-cp39-abi3-manylinux_2_28_aarch64.whl
| Download URL | cgg_callgraphgenerator-0.7.0-cp39-abi3-manylinux_2_28_aarch64.whl |
|---|---|
| Size | 10.1 MB |
| Tags | CPython 3.9 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
f706ffa975f4f6fb285e6043eebf26c264425a14f67a8f8f2b0ada0d8cd55475
|
|
BLAKE2b-256 checksum How to use checksums |
74de3ede337db4517132471f3aa7cc6813de4d18c819a11a4870e0cc3a6cfea8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cgg_callgraphgenerator-0.7.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | cgg_callgraphgenerator-0.7.0-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 10.3 MB |
| Tags | CPython 3.9 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
18de32406bb97d65229b2cce7cafa600d918ba330c98ad4f396264a8474d8fa8
|
|
BLAKE2b-256 checksum How to use checksums |
958507a853f62ceae14f466f4b7fa21f45024e62dec3a92c07c24674e8537ab5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cgg_callgraphgenerator-0.7.0-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | cgg_callgraphgenerator-0.7.0-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 10.7 MB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
9e0c46a80e968371886bef8a254398c96c554788e09696c44fe37864160b103f
|
|
BLAKE2b-256 checksum How to use checksums |
635984a93593ff74a99d80d1ac1e78cbaf395faf5ee2a0b04f1974c414d53cf4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Release files / cgg_callgraphgenerator-0.7.0-cp39-abi3-macosx_10_12_x86_64.whl
| Download URL | cgg_callgraphgenerator-0.7.0-cp39-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 10.1 MB |
| Tags | CPython 3.9 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
3f5b273d13c4022cdff2a2526775576ac0320e5576113a1832211edecf3d4198
|
|
BLAKE2b-256 checksum How to use checksums |
500fdae4616231e3adc772e2d3f81c4af2246274fdba92129e2592c0fb8f67c3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|