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.
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 aware —
can_handle()andfind_*_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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
09a9eef2eb083677cd61ff35b913308179e29f5756c3849203a87836f82400f7
|
|
| MD5 |
e190f9eada0fcc65c0ecbeaed1089117
|
|
| BLAKE2b-256 |
1a1fd0b659303c50308169b99200a81a6e42a35fff74fdda164e2711a3b93757
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5c693c609dda4d491dd57132d4b923c41e1d13b6f5630c964868839951d6c72
|
|
| MD5 |
148a6842e1e7672de14c618b45d1d1f1
|
|
| BLAKE2b-256 |
8cd2e18939de8031a25a8ce82f4352194d5c1175129aa5fdc2e845636042da9a
|