A cross-language code readability + complexity measurement instrument with a versioned metric-to-syntax specification
Project description
codecaliper
A cross-language (Python + Java, more staged) code-readability + complexity measurement instrument — not another metrics scoreboard. Documentation: https://kurathsec.github.io/codecaliper/
codecaliper is built as an instrument: every number it emits is traceable — to a versioned metric-to-syntax specification, to the exact machine-readable rulings that fired, to the exact tree-sitter grammar that parsed the source — and reproducible — clock-free, hash-seed-free, order-stable. Where a scoreboard says "CC = 7", codecaliper says "CC = 7 under spec 1.1.0, ruling CC-PY-0003, tree-sitter-python 0.25.0".
What it measures
- Readability (the core): the first open, tested, cross-language implementation of the Buse–Weimer (2010) 25-feature readability set, computed on tree-sitter (comments and positions preserved). Output is always the raw feature vector — there is deliberately no "readability score" — plus a retraining scaffold. Scalabrino/Dorn feature sets are staged for 1.x.
- Complexity (a convenience, not the selling point): cyclomatic,
dual-mode cognitive complexity (whitepaper-faithful by default,
--sonar-compatopt-in, every divergence between the modes is an enumerable spec ruling), Halstead (a declared lexical approximation), maintainability index (typed as derived from CC — never an independent signal), and the LOC family.
Why an instrument
Different tools disagree on the same file because each is a different operationalization of the same construct (radon vs lizard cyclomatic; the per-language cognitive-complexity ports; every hand-rolled Buse–Weimer extractor in the literature). codecaliper's answer:
- a versioned mapping specification — machine-readable rulings with
immutable IDs (
CC-PY-0003), shipped as package data, stamped into every result; - a hand-computed consistency corpus — every active ruling is exercised by a case with human-verified expected values;
- differential tests against radon / lizard / cognitive_complexity with a published known-divergence list — every disagreement is classified against a ruling, in both directions (an unclassified divergence fails CI, and so does a stale entry); PMD and rust-code-analysis are staged as additional oracles;
- a faithfulness reproduction of the original Buse–Weimer study (100 Java snippets, 120 annotators) using this extractor and a retrained model.
Honesty boundaries
codecaliper guarantees procedural consistency (identical rules over isomorphic syntax across languages), not cross-language numerical comparability. MI contains CC. Halstead absolute values are implementation-defined. BW output is a feature set, not a score, calibrated on 4–11-line snippets — function/file vectors are labelled extrapolation. Metrics operate on pre-preprocessing source text. Every one of these is enforced in the result types, not just stated here.
Install
pip install codecaliper # (not yet on PyPI — install from source for now)
pip install -e . # from a checkout
Use
codecaliper myfile.py --json
codecaliper src/**/*.java --csv -o metrics.csv
codecaliper myfile.py --sonar-compat --explain
codecaliper spec show CC-PY-0003 # read the ruling behind a number
codecaliper env # the calibration plate for bug reports/papers
codecaliper cite # methods-section template
from codecaliper import measure
report = measure(source_code, language="python")
cc = next(m for m in report.file_metrics if m.metric == "cyclomatic")
print(cc.value, cc.rulings, report.provenance.spec_version)
vec = report.readability[0] # raw BW 25-feature vector — never a score
print(dict(zip(vec.names, vec.values)), vec.extrapolated)
Status
Spec v1.1.0 (package 0.1.0.dev0 — the spec and package are versioned
independently): Python + Java wired end-to-end; every active ruling is
exercised by a hand-computed corpus case; mypy --strict and the differential
oracle lane (radon/lizard/cognitive_complexity, classified divergence list)
are hard CI gates. The BW faithfulness reproduction ran on the original
100-snippet dataset: 10-fold logistic accuracy 0.820 (bootstrap 95% CI
[0.770, 0.870], overlapping the paper's ~0.80), AUC 0.828, Fig. 9 sign
agreement 21/24 — after a pre-registered arbitration experiment resolved two
feature-definition ambiguities (indentation tab width; lexical fallback on
parse errors) as versioned ruling supersessions (see
validation/bw_faithfulness/derived/). The
docs site and the tag-triggered
release pipeline (PyPI trusted publishing + Zenodo archival, RELEASING.md)
are in place; next: the first tagged release (v0.1.0 + DOI) and the MSR
Data & Tool Showcase paper (ARCHITECTURE.md §16, W8).
Development
pip install -e ".[dev]" -c constraints/ci.txt
pytest
ruff check src tests tools
See ARCHITECTURE.md for the full design (data model, spec mechanism,
validation architecture, grammar-evolution policy) and CONTRIBUTING.md for
how rulings, corpus cases, and adapters are added.
Citing
Citation metadata lives in
CITATION.cff
(GitHub's "Cite this repository" box reads it; Zenodo archives releases from
it).
codecaliper cite prints a methods-section template stating the package,
spec, and grammar versions your numbers were produced under — report all
three.
License & provenance
MIT. Some measurement primitives descend from
Spaghetti Architect (same author) —
see NOTICE.
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 codecaliper-0.1.0.tar.gz.
File metadata
- Download URL: codecaliper-0.1.0.tar.gz
- Upload date:
- Size: 187.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
97e17559857eace3aca027069ee636a753bd3c6342de685c55e25ae03ec1ff6d
|
|
| MD5 |
1a0d2fb19477d5d46cec817b57125932
|
|
| BLAKE2b-256 |
b57fc7bae5e47e416a308b63ce56bcd1648fa8197af0d299754396d1b9c16a8a
|
Provenance
The following attestation bundles were made for codecaliper-0.1.0.tar.gz:
Publisher:
release.yml on KurathSec/codecaliper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codecaliper-0.1.0.tar.gz -
Subject digest:
97e17559857eace3aca027069ee636a753bd3c6342de685c55e25ae03ec1ff6d - Sigstore transparency entry: 2145787946
- Sigstore integration time:
-
Permalink:
KurathSec/codecaliper@87f8c462268c4d11dbd38126dd4373e1e845efce -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/KurathSec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@87f8c462268c4d11dbd38126dd4373e1e845efce -
Trigger Event:
push
-
Statement type:
File details
Details for the file codecaliper-0.1.0-py3-none-any.whl.
File metadata
- Download URL: codecaliper-0.1.0-py3-none-any.whl
- Upload date:
- Size: 68.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9125bf19bd3c50b4b94d0b1da84cb3d1a2ebd9d2351f03943113b9a14ab3921b
|
|
| MD5 |
77a9429e9f960b260313cfc021c93d3d
|
|
| BLAKE2b-256 |
d212d0cb9e842dcacde3e2caf1783dbdfd6f0af3912b8bac79dcfc06be26809e
|
Provenance
The following attestation bundles were made for codecaliper-0.1.0-py3-none-any.whl:
Publisher:
release.yml on KurathSec/codecaliper
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
codecaliper-0.1.0-py3-none-any.whl -
Subject digest:
9125bf19bd3c50b4b94d0b1da84cb3d1a2ebd9d2351f03943113b9a14ab3921b - Sigstore transparency entry: 2145787954
- Sigstore integration time:
-
Permalink:
KurathSec/codecaliper@87f8c462268c4d11dbd38126dd4373e1e845efce -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/KurathSec
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@87f8c462268c4d11dbd38126dd4373e1e845efce -
Trigger Event:
push
-
Statement type: