Skip to main content

Extensible polyglot code analysis framework with a graph IR

Project description

graphlens

Extensible polyglot code analysis framework that parses source projects, normalizes their structure into a shared graph IR, and exposes it for dependency analysis, navigation, and code intelligence tooling.

PyPI Python License CI codecov

Repository · Issues


Architecture

Repository → Language Adapter → GraphLens (IR) → Graph Backend
Layer Responsibility
Language Adapter Parses source files, produces GraphLens
GraphLens Typed nodes + directed relations (the IR)
Graph Backend Persists or queries the graph (Neo4j, in-memory, …)

Adapters are pure data producers — they never write to any backend. The graph is the only output.

Why graph IR?

  • Language-agnostic — one shared model for Python, TypeScript, Rust, …
  • Plugin-based adapters — each language is a separate package, registered via Python entry points
  • Tree-sitter powered — all adapters use tree-sitter for error-tolerant CST parsing and exact span positions
  • Monorepo awarecan_handle() and find_*_roots() handle multi-language repos correctly
  • Deterministic node IDs — SHA-256 hash of project::kind::qualified_name → stable across re-scans

Installation

# Core library only (models, contracts, registry)
pip install graphlens

# Core + Python adapter
pip install "graphlens[python]"

With uv:

uv add graphlens
uv add "graphlens[python]"

Quick start

from pathlib import Path
from graphlens import adapter_registry

# Load and instantiate the Python adapter
adapter = adapter_registry.load("python")()

# Analyze a project — returns a GraphLens
graph = adapter.analyze(Path("./my-project"))

print(f"Nodes:     {len(graph.nodes)}")
print(f"Relations: {len(graph.relations)}")

# Inspect nodes by kind
from graphlens import NodeKind

modules = [n for n in graph.nodes.values() if n.kind == NodeKind.MODULE]
classes = [n for n in graph.nodes.values() if n.kind == NodeKind.CLASS]

Graph model

Node kinds

Kind Description
PROJECT Root project node
MODULE Python/TS/… module (directory or file)
FILE Source file
CLASS Class declaration
FUNCTION Top-level function
METHOD Method inside a class
PARAMETER Function/method parameter
IMPORT Import statement
DEPENDENCY Declared package dependency
SYMBOL Internal symbol reference
EXTERNAL_SYMBOL External symbol (stdlib, third-party, unknown)

Relation kinds

Kind Description
CONTAINS Structural containment (project → module → file → class)
DECLARES Declaration (file declares function, class declares method)
IMPORTS Import edge (file → import node)
RESOLVES_TO Import resolved to a module or external symbol
CALLS Function/method call
REFERENCES Symbol reference
INHERITS_FROM Class inheritance
DEPENDS_ON Package dependency

Adapter plugin system

Language adapters register themselves via Python entry points — no changes to the core needed:

# packages/graphlens-python/pyproject.toml
[project.entry-points."graphlens.adapters"]
python = "graphlens_python:PythonAdapter"

The registry discovers installed adapters automatically at runtime:

from graphlens import adapter_registry

adapter_registry.available()          # ["python", ...]
adapter_cls = adapter_registry.load("python")
adapter = adapter_cls()

Adapters can also be registered manually (useful for testing):

adapter_registry.register("python", MyPythonAdapter)

Implementing an adapter

Subclass LanguageAdapter and implement four methods:

from pathlib import Path
from graphlens import GraphLens, LanguageAdapter

class MyLangAdapter(LanguageAdapter):
    def language(self) -> str:
        return "mylang"

    def file_extensions(self) -> set[str]:
        return {".ml", ".mli"}

    def can_handle(self, project_root: Path) -> bool:
        return (project_root / "dune-project").exists()

    def analyze(
        self, project_root: Path, files: list[Path] | None = None
    ) -> GraphLens:
        graph = GraphLens()
        files = files or self.collect_files(project_root)
        # ... parse and populate graph ...
        return graph

Register in pyproject.toml and the core registry finds it automatically.

Project structure

graphlens/                      ← uv workspace root (core library)
  src/graphlens/                ← models, contracts, registry, exceptions, utils
  packages/
    graphlens-python/           ← Python adapter (tree-sitter)
  tests/                         ← core tests (100% coverage)
  examples/                      ← runnable usage examples

Development

Requires Python 3.13+, uv, task.

task install        # uv sync --all-groups
task lint           # ruff + ty + bandit for all packages
task tests          # all tests with coverage

Individual package tasks:

task core:lint      task core:test
task python:lint    task python:test

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

graphlens-0.2.2.tar.gz (9.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

graphlens-0.2.2-py3-none-any.whl (14.3 kB view details)

Uploaded Python 3

File details

Details for the file graphlens-0.2.2.tar.gz.

File metadata

  • Download URL: graphlens-0.2.2.tar.gz
  • Upload date:
  • Size: 9.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for graphlens-0.2.2.tar.gz
Algorithm Hash digest
SHA256 09a9eef2eb083677cd61ff35b913308179e29f5756c3849203a87836f82400f7
MD5 e190f9eada0fcc65c0ecbeaed1089117
BLAKE2b-256 1a1fd0b659303c50308169b99200a81a6e42a35fff74fdda164e2711a3b93757

See more details on using hashes here.

File details

Details for the file graphlens-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: graphlens-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 14.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.3 {"installer":{"name":"uv","version":"0.11.3","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for graphlens-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 e5c693c609dda4d491dd57132d4b923c41e1d13b6f5630c964868839951d6c72
MD5 148a6842e1e7672de14c618b45d1d1f1
BLAKE2b-256 8cd2e18939de8031a25a8ce82f4352194d5c1175129aa5fdc2e845636042da9a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page