Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Darwinius

Darwinius finds the files in your Python codebase that are most likely to cause pain: the ones that change constantly and are tightly coupled to everything else.

It does this by combining two signals from your Git history:

  • Churn — how often a file changes (from git log)
  • Coupling — how many other files import it, and how many things it depends on (from ast.parse)

Files that score high on both are your hotspots — the first places to refactor.

What the output looks like

Running darwinius analyze produces:

  • report.html — an interactive report with core hotspots, stable hubs, test hotspots, registry/aggregator detection, risk histogram, coupling evolution chart, and dependency graph
  • files.csv — every Python file ranked by risk score, with a category of source, test, or registry
  • coupling.csv — every import relationship and when it first appeared
  • changes.csv — full commit history per file
  • darwinius.db — the raw SQLite database (re-run reports without re-analyzing)

How risk is calculated

instability  = fan_out / (fan_in + fan_out)   # Robert Martin's I metric
churn_score  = commit_count / max_commit_count # normalized 0–1
risk_score   = sqrt(churn_score × instability)

A file scores high only when it's both high-churn and unstably coupled — not just one or the other. Pure utilities that never change score low even if everything imports them. Frequently changed files that nobody depends on also score low.

Files that look like import registries or aggregators — for example files with unusually high fan-out such as extractor registries — are shown in a separate report section instead of being mixed into core refactor candidates. Test hotspots and stable hubs are also separated so the main table stays focused on source files that are plausible refactor targets.

Installation

Requirements: Python 3.9+, Git

git clone https://github.com/yourname/darwinius
cd darwinius
python3 -m venv .venv
source .venv/bin/activate       # Windows: .venv\Scripts\activate
pip install -e .

To render the dependency graph SVG in the HTML report, also install Graphviz:

brew install graphviz            # macOS
sudo apt install graphviz        # Ubuntu/Debian

Usage

Analyze a repo

darwinius analyze /path/to/your/repo

Output goes to /path/to/your/repo/darwinius_out/. Open report.html in a browser.

Common options

# Custom output directory
darwinius analyze /path/to/repo -o ./results

# Only look at commits since a date
darwinius analyze /path/to/repo --since 2023-01-01

# Analyze a specific branch
darwinius analyze /path/to/repo --branch main

# Skip temporal coupling analysis (faster, no evolution chart)
darwinius analyze /path/to/repo --skip-temporal

# Show 100 files instead of 50, require at least 10 commits to count as a hotspot
darwinius analyze /path/to/repo --top-n 100 --min-churn 10

# Focus the report on production code only
darwinius analyze /path/to/repo --exclude-tests

# CSV only, no HTML
darwinius analyze /path/to/repo --no-html

# Re-run everything from scratch
darwinius analyze /path/to/repo --force

Darwinius prefers Git-tracked Python files when analyzing a Git repo. Outside a Git repo, it skips common generated and environment directories such as .venv/, venv/, .git/, build/, dist/, .pytest_cache/, and __pycache__/.

Re-generate a report from an existing DB

Re-runs the report without re-parsing Git history:

darwinius report ./results/darwinius.db -o ./results --format html

Add --exclude-tests to regenerate a production-only report from the same database.

Inspect a single file

darwinius inspect src/models/user.py --db ./results/darwinius.db

Prints the file's risk score, fan-in, fan-out, what it imports internally, and what imports it.

How it works

Git Repo
  ↓
git_analyzer.py     — walks git log, extracts commits + per-file churn → SQLite
  ↓
python_analyzer.py  — parses .py files with ast.parse(), extracts imports → SQLite
  ↓
temporal_analyzer.py — replays commits chronologically, tracks when coupling
                        edges appear and disappear → SQLite
  ↓
metrics.py          — computes risk scores per file → SQLite
  ↓
report_generator.py — renders report.html + CSV files

Each step writes to a SQLite database (darwinius.db). Re-running skips already-complete steps automatically. Use --force to re-run everything.

Performance

On a 50k-commit repo, the Git and import phases run in under 5 minutes. The temporal replay (tracking coupling over every commit) is the expensive step — use --skip-temporal to skip it if you only need the current-state hotspot ranking.

Running tests

pip install -e ".[dev]"
pytest tests/ -v

What it doesn't do

  • Multi-language support — Python only (uses ast.parse). Non-Python files are ignored.
  • Dynamic importsimportlib.import_module("x") and string-based imports are not detected.
  • Real-time analysis — it's a batch tool; re-run it when you want fresh data.
  • Fully offline reportsreport.html is a single local file, but charts load Chart.js from jsDelivr.

About

Darwinius finds the files in your Python codebase that are most likely to cause pain: the ones that change constantly and are tightly coupled to everything else.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages