Object-oriented software quality metrics for Python projects, computed from the
ast. It implements the Chidamber & Kemerer suite (DIT, RFC, NOC, CBO, LCOM),
the Li & Henry maintainability metrics (MPC, SIZE2, WAC) and the related size /
complexity counts (LOC, NOM, NOCC, WMPC1, WMPC2).
Every reported value, with the formula as actually implemented (which is not
always the textbook definition), is in docs/metrics.md.
It ships three ways to use it: a Python API (analyze(...)), a command-line tool
(metrics-calculator), and a desktop app (metrics-calculator-gui).
| Per-class results table | Filter + export |
|---|---|
![]() |
![]() |
| DIT distribution, outliers highlighted | Registry-generated metrics manual |
![]() |
![]() |
Requires Python 3.11, 3.12 or 3.13. The project is not on PyPI yet, so install from the Git repository.
pipx install "metrics-calculator-python[cli] @ git+https://github.com/minaschar/metrics-calculator-python.git"
# for the desktop app instead:
pipx install "metrics-calculator-python[cli,gui] @ git+https://github.com/minaschar/metrics-calculator-python.git"python -m venv .venv && . .venv/bin/activate # Windows: .venv\Scripts\activate
pip install "metrics-calculator-python[cli,gui] @ git+https://github.com/minaschar/metrics-calculator-python.git"Extras: cli pulls in Typer/Rich/pandas/openpyxl for the command-line tool and
.xlsx export; gui pulls in PySide6 for the desktop app. The bare core (the
analyze() API) has no dependencies beyond the standard library.
metrics-calculator analyze path/to/project # Rich table on stdout
metrics-calculator analyze path/to/project -f json -o out.json # table | json | csv | html | xlsx
metrics-calculator analyze path/to/project --fail-under RFC=40 --fail-under CBO=15
metrics-calculator diff old_run new_run # dir-or-JSON vs dir-or-JSON
metrics-calculator --help--fail-under METRIC=MAX(repeatable) makes it a CI quality gate.diffcompares two runs; each side is a project directory (analysed on the spot) or a JSON file previously written withanalyze -f json.- Scope with
--include/--excludeglob options, or a[tool.metrics_calculator]table in the target project'spyproject.toml(or a standalonemetrics-calculator.toml). By default the scanner skips.venv/,site-packages/,__pycache__/,node_modules/,build/,dist/and similar. - Exit codes:
0success ·1a--fail-underthreshold was exceeded (ordifffound changes) ·2bad usage. - The
-f jsonpayload carries"schema_version": 1; per-class rows are keyed by the metric abbreviations.
metrics-calculator-gui # or: python -m metrics_calculator.gui
metrics-calculator-gui path/to/projectPick a project; analysis runs off the UI thread with a progress bar and Cancel. Results land in a sortable / filterable table with threshold and outlier colouring, plus per-metric distribution histograms, drill-down from a class to its source, a diagnostics panel and a registry-generated metrics manual. Light / dark theme, remembered between runs.
from metrics_calculator import analyze, AnalysisConfig
result = analyze("path/to/project", AnalysisConfig())
for file in result.files:
for cls in file.classes:
print(cls.class_name, cls.complexity.dit, cls.cohesion.lcom)
for diag in result.diagnostics: # files that were skipped / could not parse
print(diag)analyze(path, config) -> ProjectMetrics is the whole public surface; it imports
no UI code.
- Python 3.11–3.13
uv— manages the virtualenv, the lockfile and every dev command
git clone https://github.com/minaschar/metrics-calculator-python.git
cd metrics-calculator-python
uv sync --all-extras # creates .venv with core + cli + gui + dev tools
uv run pre-commit install # optional: run the checks on every commituv run metrics-calculator analyze . # run the CLI against this repo
uv run metrics-calculator-gui . # run the desktop app
uv run pytest # tests
uv run ruff check . # lint
uv run ruff format --check . # formatting (drop --check to apply)
uv run mypy # types (strict)CI runs exactly uv run ruff check ., uv run ruff format --check .,
uv run mypy and uv run pytest -v on Python 3.11 / 3.12 / 3.13; pre-commit
runs the same tools through uv so versions match the lockfile.
uv run python -m metrics_calculator.docs # refresh docs/metrics.md
SNAPSHOT_UPDATE=1 uv run pytest tests/test_engine_snapshots.py # re-record fixture snapshots
uv run pytest tests/test_engine_snapshots.py # then review the diffdocs/metrics.md and tests/snapshots/*.json are committed and checked by tests;
never hand-edit them. Metric abbreviations (LOC, NOM, SIZE2, …) are frozen.
uv run pyinstaller --clean --noconfirm packaging/metrics-calculator-gui.spec
# -> dist/MetricsCalculator[.exe]src/metrics_calculator/
__init__.py public API: analyze(), the result dataclasses, the registry
engine.py orchestration: discover -> extract -> compute per class
discovery.py file walk + parse, skipped files reported as diagnostics
extraction.py structural facts per class (methods, fields, bases)
analysis/ one module per metric family (loc, complexity, cohesion, method_calls)
registry.py one entry per metric: name, category, description, formula, source
reporting.py ProjectMetrics -> csv / json / html / xlsx (shared with the GUI)
thresholds.py --fail-under parsing + evaluation
docs.py regenerates docs/metrics.md from the registry
cli/ Typer command-line app
gui/ PySide6 desktop app (imports nothing back into the core)
tests/
fixtures/<name>/ tiny sample projects analysed by the snapshot suite
snapshots/<name>.json the expected per-class table for each fixture
- New metric: add a field to the relevant dataclass in
results.py, compute it inengine.py(or a newanalysis/module), and add oneMetricDefinitiontoregistry.py(formula= what the code actually does, plusnotesfor any approximation). The CLI table, every export format, the desktop manual anddocs/metrics.mdpick it up automatically. Then run the two regen commands above. - Changed value: metric semantics are otherwise frozen. Add a failing test, make the fix, regenerate the affected snapshots, and show the snapshot diff in the commit.
- The public abbreviations (LOC, NOM, SIZE2, WAC, NOCC, DIT, WMPC1, WMPC2, RFC, CBO, MPC, LCOM, NOC) are research vocabulary — never rename them.
- Parses with the running interpreter's
ast; a file with a syntax error is skipped and reported as a diagnostic. async defmethods, comprehensions andmatchstatements are handled.- Coupling is name-based. Remote calls and base classes are matched by name,
with no type resolution, so a local variable and an unrelated class that share
a name are conflated — inherent to the C&K / Li–Henry approximations, and
called out per metric in
docs/metrics.md. - Cyclomatic complexity is approximate: control-flow keywords and
matchcases count;and/or/except/assertdo not. - DIT counts only inheritance within the analysed project.
- QMOOD design-quality attributes (Bansiya & Davis 2002): reusability,
flexibility, understandability, functionality, extendability, effectiveness.
Not computed; listed in
docs/metrics.mdfor completeness. - Technical-debt scoring. There is no technical-debt model in this tool.
EPL-2.0. See LICENSE.



