Codebase metrics and analysis tool for Python projects
Project description
Pymetrica
Pymetrica is a static analysis tool that computes software engineering metrics for Python codebases.
It parses Python source code using the AST (Abstract Syntax Tree) and evaluates classical metrics used to assess complexity, maintainability, and architectural stability.
The tool provides a modular architecture, a CLI interface, a reusable Python API, and extensible reporting to help developers understand the structural quality of their Python projects.
Repository: https://github.com/JuanJFarina/pymetrica
Example
Analyze a Python project:
pymetrica run-all path/to/project
By default, run-all emits a short terminal report. Use --long-report
when you want descriptive summaries and per-layer breakdowns. Use
-rt BASIC_HOOK when you want thresholds to produce a non-zero exit status
for CI or pre-commit.
Example short report:
----------------------------------------------------------------------------------------------------
Short Report
----------------------------------------------------------------------------------------------------
Metric: Abstract Lines Of Code
aloc_number: 6
aloc_percentage: 15.0
----------------------------------------------------------------------------------------------------
Metric: Cyclomatic Complexity
cc_number: 23
lloc_per_cc: 1.7391304347826086
----------------------------------------------------------------------------------------------------
Metric: Halstead Volume
hv_number: 704.5342159112735
hv_per_lloc: 17.613355397781838
----------------------------------------------------------------------------------------------------
Metric: Primitive Obsession
all_primitives_percent: 0.0
targeted_primitives_percent: 0.0
----------------------------------------------------------------------------------------------------
Metric: Maintainability Cost
maintainability_cost: 50.678396768622775
raw_line_cost: 50.638396768622776
----------------------------------------------------------------------------------------------------
Metric: Instability
root: 0.0
----------------------------------------------------------------------------------------------------
Pymetrica can also analyze architecture layers and dependencies within the codebase.
If you switch to a single-metric command such as pymetrica cc path/to/project,
Pymetrica always prints the descriptive report format instead of this short
layout.
Contents
- Features
- Why Pymetrica
- Metrics
- Installation
- Quick Start
- CLI Commands
- Configuration and Hooks
- Architecture Overview
- Python API
- Architecture Diagram Generation
- Testing
- Contributing
- License
Features
- Static analysis of Python projects using the AST
- Logical Lines of Code (LLOC) analysis
- Comment density statistics
- Layered architecture detection based on directories
- Multiple classical software engineering metrics
- CLI interface for fast inspection of codebases
- Optional Mermaid architecture diagrams for top-level layers and components
- Configurable thresholds and file exclusion patterns from
pyproject.toml - Published
pre-commithooks for automated metric checks - Reusable Python API for parser, calculators, and report generation
- Extensible metric and reporting system
Why Pymetrica?
Several tools compute Python complexity metrics (such as radon, lizard, or SonarQube integrations). Pymetrica focuses on a different goal: architecture-aware metric analysis.
Unlike many static analysis tools, Pymetrica:
- groups metrics by codebase layers derived from directory structure
- computes cross-layer coupling and instability metrics
- produces architecture diagrams alongside metric results
- provides a modular framework for implementing new metrics
This makes it useful not only for measuring complexity, but also for analyzing architectural quality in Python projects.
Metrics
Pymetrica implements several classical software engineering metrics.
Abstract Lines of Code (ALOC)
Measures the amount of abstraction and indirection in the codebase by counting abstract constructs such as definitions and structural components.
High ALOC ratios may indicate excessive abstraction or over-engineering.
Cyclomatic Complexity (CC)
Measures the number of independent execution paths in a program.
Calculated by analyzing control flow structures including:
- conditionals
- loops
- exception handling
- boolean logic
- comprehensions, assertions, context managers, and structural pattern matching
Higher values correspond to more complex and harder-to-maintain code.
Halstead Volume (HV)
Measures implementation complexity based on operators and operands used in the program.
Derived from:
- program vocabulary
- program length
- token frequency
The current visitor counts Python AST operators such as assignments, arithmetic and boolean operations, comparisons, control-flow keywords, function and class definitions, and operands such as names, attributes, and constants.
Primitive Obsession (PO)
Highlights type annotations that rely heavily on primitive types instead of domain-specific abstractions.
The current implementation counts primitive scalar annotations such as int,
float, bool, str, and Any, plus targeted container annotations such as
dict, list, tuple, and set. Any is also treated as targeted.
PEP 604 | unions are counted when every member resolves to one of the
supported primitive or container forms. Unsupported or custom annotation forms
are ignored rather than reported as primitive usage.
Maintainability Cost (MC)
A composite metric derived from:
- Cyclomatic Complexity
- Halstead Volume
- Logical Lines of Code
It estimates the expected maintenance effort required for the codebase. The score combines Halstead density, CC density, and a small LLOC-based size penalty.
Lower scores indicate better maintainability.
Instability (LI)
Measures package coupling and architectural stability based on import dependencies.
Instability is defined as:
Instability = Efferent Coupling / (Afferent Coupling + Efferent Coupling)
Values range from:
- 0 → Stable
- 1 → Unstable
Installation
Requires Python 3.10 or newer.
Install the latest published package from PyPI:
pip install pymetrica
Or install from source:
git clone https://github.com/JuanJFarina/pymetrica
cd pymetrica
pip install -e .
After installation the CLI command becomes available:
pymetrica
Quick Start
Analyze a Python project:
pymetrica run-all path/to/project
Configured [tool.pymetrica].exclude patterns are applied before the codebase
is parsed. To enforce thresholds in automation, use the hook report backend:
pymetrica run-all -rt BASIC_HOOK path/to/project
For an initial overview of a codebase:
pymetrica base-stats path/to/project
To focus on one metric, run its dedicated command:
pymetrica cc path/to/project
Single-metric commands always use the descriptive report format.
CLI Commands
pymetrica status
pymetrica base-stats
pymetrica aloc
pymetrica cc
pymetrica hv
pymetrica po
pymetrica mc
pymetrica li
pymetrica run-all
Typical usage pattern:
pymetrica <command> DIR_PATH
Notes:
run-allsupports--long-reportfor descriptive summaries and per-layer detailaloc,cc,hv,po,mc, andlialways emit the descriptive report format- all parsing commands honor
[tool.pymetrica].excludepatterns
Configuration and Hooks
Pymetrica reads optional thresholds and exclusion patterns from
[tool.pymetrica] in pyproject.toml:
[tool.pymetrica]
aloc_fail_threshold = 30
cc_fail_threshold = 10
hv_fail_threshold = 30
po_all_fail_threshold = 10
po_targeted_fail_threshold = 2
mc_fail_threshold = 25
exclude = ["generated/*", "vendor/*"]
top_findings = 5
Built-in threshold defaults are active: 30 for ALOC, 7 for CC, 30 for
HV, 10 for all primitives, 2 for targeted primitives, and 25 for MC.
Set a threshold to 0 to disable failure gating for that metric. exclude
defaults to an empty list, and top_findings defaults to 5.
Important details:
- exclusions are matched against paths relative to the resolved analysis root
- matching uses Python's
fnmatch - exclusions skip matching Python files during parsing; layer discovery and folder counts can still include excluded directories
- thresholds are enforced by the
BASIC_HOOKreport backend;BASIC_TERMINALprints values and exits with0 cc_fail_thresholdfails whenlloc_per_ccfalls below the configured valuerun-allcombines threshold failures with exit-code weights1for ALOC,2for CC,4for HV,8for MC, and16for PO- single-metric commands using
BASIC_HOOKreturn their metric-specific exit code when the threshold fails top_findings = 0disables top-finding lists in failure messages
Pymetrica also publishes pre-commit hooks:
repos:
- repo: https://github.com/JuanJFarina/pymetrica
rev: v1.5.2
hooks:
- id: pymetrica
- id: pymetrica-mc
- id: pymetrica-po
Available hook IDs today:
pymetricapymetrica-alocpymetrica-ccpymetrica-hvpymetrica-popymetrica-mc
Architecture Overview
Pymetrica is built around a modular analysis pipeline.
Codebase Parsing
↓
Code Representation
↓
Metric Calculators
↓
Results
↓
Report Generators
Core components include:
Parser
Recursively scans .py files and builds a structured representation of the codebase.
Extracted information includes:
- logical lines of code
- comment lines
- classes and functions
- directory structure
Files containing syntax errors are automatically skipped.
Data Models
Core data structures are implemented using Pydantic models.
Main models include:
Code– representation of a Python fileCodebase– full project structureMetric– container for metric metadata and resultsResults– structured metric outputs
Metric Calculators
Each metric is implemented as a subclass of an abstract MetricCalculator.
This design makes it easy to extend the system with additional metrics.
Reporting
Metrics are rendered through pluggable report generators.
Currently supported:
BASIC_TERMINALterminal reports, with short and detailed layouts and a zero exit statusBASIC_HOOKhook-oriented reports that show only failed metrics and return threshold-based exit statuses
Future formats may include JSON, Markdown, or CI-friendly outputs.
Python API
The CLI and the Python API share the same parser, calculators, and report registry. A typical programmatic workflow is:
from pymetrica.codebase_parser import create_diagram, parse_codebase
from pymetrica.metric_calculators import (
AlocCalculator,
CCCalculator,
HalsteadVolumeCalculator,
InstabilityCalculator,
MaintainabilityCostCalculator,
PrimitiveObsessionCalculator,
)
from pymetrica.report_generators import REPORTS_MAPPING
codebase = parse_codebase("path/to/project")
metrics = [
AlocCalculator().calculate_metric(codebase),
CCCalculator().calculate_metric(codebase),
HalsteadVolumeCalculator().calculate_metric(codebase),
PrimitiveObsessionCalculator().calculate_metric(codebase),
MaintainabilityCostCalculator().calculate_metric(codebase),
InstabilityCalculator().calculate_metric(codebase),
]
report_generator = REPORTS_MAPPING["BASIC_TERMINAL"](metrics)
print(report_generator.long_report.content)
create_diagram(codebase, filename="architecture.mmd")
parse_codebase() uses the same exclusion rules as the CLI, so configured
[tool.pymetrica].exclude patterns still apply.
Architecture Diagram Generation
Pymetrica can generate Mermaid diagrams representing the layered architecture of a codebase.
pymetrica base-stats --diagram path/to/project
This creates a .mmd file that can be rendered using:
- Mermaid Live Editor
- VSCode Mermaid extensions
- documentation pipelines
The diagram focuses on top-level layers and components. Root-level files are
omitted, and __init__.py-style files are not emitted as components.
Testing
Tests are implemented using pytest and mirror the project structure.
Run tests with:
pytest
Contributing
Contributions are welcome.
If you want to:
- implement a new metric
- improve the parser
- extend reporting capabilities
feel free to open an issue or submit a pull request.
License
MIT License.
Project details
Release history Release notifications | RSS feed
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 pymetrica-1.5.3.tar.gz.
File metadata
- Download URL: pymetrica-1.5.3.tar.gz
- Upload date:
- Size: 80.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
50d8bd9064055c5bf4d6d60f71917066d496cb90dbaca8ea4ec2b48d8fa76c12
|
|
| MD5 |
e0f169824802afe5e5db149f9a31d2e6
|
|
| BLAKE2b-256 |
559ff0072110ea644ac80945b2b9b386ded5a1e763c3216055984cef9feb6933
|
Provenance
The following attestation bundles were made for pymetrica-1.5.3.tar.gz:
Publisher:
deploy.yml on JuanJFarina/pymetrica
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymetrica-1.5.3.tar.gz -
Subject digest:
50d8bd9064055c5bf4d6d60f71917066d496cb90dbaca8ea4ec2b48d8fa76c12 - Sigstore transparency entry: 1446857299
- Sigstore integration time:
-
Permalink:
JuanJFarina/pymetrica@391d2ffbbfe2b597d9ae49be0cb610d2d3354e84 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JuanJFarina
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@391d2ffbbfe2b597d9ae49be0cb610d2d3354e84 -
Trigger Event:
workflow_run
-
Statement type:
File details
Details for the file pymetrica-1.5.3-py3-none-any.whl.
File metadata
- Download URL: pymetrica-1.5.3-py3-none-any.whl
- Upload date:
- Size: 73.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
62a685f098e6ad2b7c34cc3e22de11572ce028f449e81a47b651c5f6d3464d48
|
|
| MD5 |
b880bf191c8a60192421573c1f7c17ff
|
|
| BLAKE2b-256 |
650fbc52205eaedf72a8069f78ca62cb2ba82b86ba736f15103d7b2c74d7e299
|
Provenance
The following attestation bundles were made for pymetrica-1.5.3-py3-none-any.whl:
Publisher:
deploy.yml on JuanJFarina/pymetrica
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pymetrica-1.5.3-py3-none-any.whl -
Subject digest:
62a685f098e6ad2b7c34cc3e22de11572ce028f449e81a47b651c5f6d3464d48 - Sigstore transparency entry: 1446857438
- Sigstore integration time:
-
Permalink:
JuanJFarina/pymetrica@391d2ffbbfe2b597d9ae49be0cb610d2d3354e84 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/JuanJFarina
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
deploy.yml@391d2ffbbfe2b597d9ae49be0cb610d2d3354e84 -
Trigger Event:
workflow_run
-
Statement type: