Skip to main content

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 cgg module: do not install both into the same environment. Both write to site-packages/cgg/, pip will not stop you, and whichever lands second overwrites the first. If you already have pip install cgg (the GGUF tool), use a separate virtualenv.

One wheel per platform covers every CPython ≥ 3.9 (the extension is built against the stable ABI).

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
g.to_json()           # str — identical to `cgg -t json`
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 tree (1,949 callables): to_mermaid() produces 175 KB in 2.9 ms, and the first .callables access costs 1.6 ms. 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 and there is no internal lock, so a thread pool scales. Measured on crates/cgg-lang/src/plugins, against a single analysis alone:

threads wall vs. one analysis
1 53 ms 0.99x
2 65 ms 1.21x
4 72 ms 1.34x
8 91 ms 1.68x

Four analyses for 1.34x the wall clock of one; eight for 1.68x. Absolute numbers are machine-specific — 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, the audit event stream, and the framework-coverage table are all reachable from the Rust API but are not yet exposed here. Use the CLI for those.

Building from source

scripts/build-python.sh          # from the repository root

Needs a Rust toolchain (≥ 1.85) and uv.

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

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cgg-callgraphgenerator 0.6.3
File Size Uploaded
cgg_callgraphgenerator-0.6.3.tar.gz 566.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cgg-callgraphgenerator 0.6.3
File Interpreter ABI Platform
cgg_callgraphgenerator-0.6.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.9 abi3 Linux glibc 2.17+ x86-64 Details

Total release size: 10.7 MB

Release files / cgg_callgraphgenerator-0.6.3.tar.gz

Download URL cgg_callgraphgenerator-0.6.3.tar.gz
Size 566.6 kB
Tags Source
SHA-256 checksum
How to use checksums
3ba3d7a869d42aea7155e300e72d998a73bbba0f46e9b60c14aaf6dc7204acce
BLAKE2b-256 checksum
How to use checksums
ac6740819090ffac5d1c600fae54a0e061814ff1cb4454c35b29c594261c1cc1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release files / cgg_callgraphgenerator-0.6.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL cgg_callgraphgenerator-0.6.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 10.1 MB
Tags CPython 3.9 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
97ea416680e96b34dcfcf1c8f4a748c99d23ad65b0483eccb57a48c6d1500e92
BLAKE2b-256 checksum
How to use checksums
eb1e0225f019c28048a436342405fbe6d79c26494460c9f00ba6207ef1fcedc3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.3

Release history Release notifications | RSS feed

0.10.0

6 release files

0.9.1

6 release files

0.9.0

6 release files

0.8.5

6 release files

0.8.4

6 release files

0.8.3

6 release files

0.8.2

6 release files

0.8.1

6 release files

0.8.0

6 release files

0.7.0

6 release files

0.6.7

6 release files

0.6.6

6 release files

0.6.5

6 release files

0.6.4

6 release files

This release

0.6.3 This release

2 release files

0.6.2

1 release file

0.6.1

2 release files

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