Skip to main content

Dependency Update Metrics

A Python tool for analyzing time-to-update (TTU) and time-to-remediate (TTR) metrics for package dependencies across different ecosystems (npm, PyPI, Cargo/crates.io).

Features

  • Multi-ecosystem support: Analyze packages from npm, PyPI, and Cargo/crates.io
  • Time-to-Update (TTU): Measure how long dependencies stay behind the latest version
  • Time-to-Remediate (TTR): Measure how long known vulnerabilities remain unpatched
  • Flexible weighting: Support for linear, exponential, inverse, or no weighting
  • OSV integration: Uses the Open Source Vulnerabilities database for security analysis
  • Export capabilities: Export detailed worksheets and OSV data

Installation

From PyPI

pip install dependency-update-metrics

From source

git clone https://github.com/imranur-rahman/dependency-update-metrics.git
cd dependency-update-metrics
pip install -e .

With Docker (exact reruns)

The container image ghcr.io/imranur-rahman/mttx holds the Tool with pinned dependencies and the patched pip, so a rerun years later uses the same environment:

docker run --rm -v "$PWD:/data" ghcr.io/imranur-rahman/mttx:<version>-osv-<YYYY-MM-DD> \
  --package express --ecosystem npm --start-date 2024-01-01 --end-date 2025-01-01
  • mttx:<version> (and latest) has no OSV index. MTTR needs --build-osv once, as with pip.
  • mttx:<version>-osv-<YYYY-MM-DD> holds the OSV index built on that date, so MTTR is the same in each rerun.

The results and the cache go to /data. For exact reruns, cite the image digest (ghcr.io/imranur-rahman/mttx@sha256:…), not the tag. See image/README.md.

Usage

As a command-line tool

# Basic usage
dependency-metrics --ecosystem npm --package express

# With date range
dependency-metrics --ecosystem npm --package express \
  --start-date 2020-01-01 \
  --end-date 2023-12-31

# With exponential weighting
dependency-metrics --ecosystem npm --package express \
  --weighting-type exponential \
  --half-life 180

# Build OSV database first (one-time operation)
dependency-metrics --ecosystem npm --package express --build-osv

# Export detailed worksheets
dependency-metrics --ecosystem npm --package express --get-worksheets

# Get OSV vulnerability data
dependency-metrics --ecosystem npm --package express --get-osv

# Test a PyPI package
dependency-metrics --ecosystem pypi --package requests --start-date 2020-01-01 --end-date 2021-01-01 --get-worksheets

# Test a Cargo/crates.io package
dependency-metrics --ecosystem cargo --package serde --start-date 2020-01-01 --end-date 2024-01-01

# Bulk CSV input (parallel by default)
dependency-metrics --input-csv ./input.csv --workers 8 --output-dir ./output

# Resume an interrupted bulk run
dependency-metrics --input-csv ./input.csv --output-dir ./output --resume

# Per-release analysis: MTTU/MTTR at every release of the parent package
dependency-metrics --ecosystem pypi --package requests \
  --start-date 2020-01-01 --end-date 2021-01-01 \
  --per-release --get-worksheets

# Per-release in bulk CSV mode
dependency-metrics --input-csv ./input.csv --per-release --workers 4

Input CSV columns: ecosystem, package_name, optional start_date, optional end_date. Accepted ecosystem values are npm, pypi, cargo, and crates.io; crates.io is normalized to cargo in outputs. If end_date is omitted, the CLI --end-date value is used; when --end-date is also omitted, it defaults to today. Extra columns are allowed; duplicates are removed by ecosystem, package_name, end_date. Bulk mode computes metrics per package using the latest dependency set.

In GitHub Actions

The MTTX Action runs the Tool image in a workflow. It reports the MTTU and MTTR of chosen packages (with badges and thresholds), or comments on a pull request with the MTTU and MTTR of the dependencies that it adds. See action/README.md.

- uses: imranur-rahman/mttx-action@v1
  with:
    packages: express, koa
    ecosystem: npm
    max-mttr-days: 30

As a Python module

from datetime import datetime
from pathlib import Path
from dependency_metrics.analyzer import DependencyAnalyzer

# Create analyzer
analyzer = DependencyAnalyzer(
    ecosystem="npm",
    package="express",
    start_date=datetime(2020, 1, 1),
    end_date=datetime(2023, 12, 31),
    weighting_type="exponential",
    half_life=180,
    output_dir=Path("./output")
)

# Run analysis
results = analyzer.analyze()

print(f"Average TTU: {results['ttu']:.2f} days")
print(f"Average TTR: {results['ttr']:.2f} days")

Command-line Arguments

  • --ecosystem: Ecosystem to analyze (npm, pypi, cargo, or crates.io) [Required unless --input-csv]
  • --package: Package name to analyze [Required unless --input-csv]
  • --input-csv: CSV file with columns ecosystem, package_name, optional start_date, and optional end_date. If end_date is omitted, --end-date is used.
  • --start-date: Start date for analysis (YYYY-MM-DD) [Default: 1900-01-01]
  • --end-date: End date for analysis (YYYY-MM-DD) [Default: today]
  • --weighting-type: Weighting method (linear, exponential, inverse, disable) [Default: disable]
  • --half-life: Half-life in days (required for exponential weighting)
  • --build-osv: Build or rebuild the OSV vulnerability database. It downloads the npm, PyPI, and crates.io OSV zips (about 260 MB in total). Without --package, it only rebuilds the database.
  • --osv-index-date: Print the build date of the OSV database as JSON (null when there is no database), then exit
  • --cache-stats: Show the rows and the size of each namespace in the resolver cache (<output-dir>/cache/cache.db), then exit
  • --cache-prune-gb N: Remove the least recently used cache rows until the cache holds less than N GB of data, then exit. The prune does not run VACUUM, so the file does not shrink; SQLite reuses the freed pages
  • --cache-clear: Remove all cache rows, then exit
  • --cache-namespace X: With --cache-clear, remove only the rows of namespace X
  • --get-osv: Export OSV data for the package's dependencies
  • --get-worksheets: Export detailed analysis worksheets to Excel
  • --output-dir: Output directory for results [Default: ./output]
  • --workers: Number of parallel workers for bulk CSV mode [Default: min(8, CPU count)]
  • --package-timeout: Per-package analysis timeout in seconds (bulk CSV mode only). Packages that exceed this time receive an error row and are skipped [Default: disabled]
  • --resume: Resume bulk CSV runs by skipping completed rows and retrying errors
  • --per-release: Compute MTTU/MTTR at every release of the parent package within the window (one output row per release)
  • --json: Print the result as one JSON object on stdout, in the result shape of the Host API (needs --package; see USAGE.md)

Weighting Methods

Disable (default)

No weighting applied. All time periods are weighted equally.

Linear

Weight decreases linearly with age:

weight = 1 - (age / max_age)

Exponential

Weight decreases exponentially based on half-life:

weight = exp(-λ * age)
where λ = ln(2) / half_life

Inverse

Weight is inversely proportional to age:

weight = 1 / (1 + age)

Output

The tool generates several outputs in the specified output directory:

  1. JSON results file: Contains TTU, TTR, and metadata
  2. OSV data (with --get-osv): CSV file with vulnerability information
  3. Excel worksheets (with --get-worksheets): Detailed analysis for each dependency
  4. Bulk CSV results (with --input-csv): Summary CSV, plus an optional dependency details CSV with --write-dependency-details
  5. Per-release results (with --per-release): One row per package release with package_version, package_release_date, window_start, window_end, mttu, mttr

Example JSON output

{
  "package": "express",
  "ecosystem": "npm",
  "version": "4.18.2",
  "start_date": "2020-01-01T00:00:00",
  "end_date": "2023-12-31T00:00:00",
  "weighting_type": "exponential",
  "half_life": 180,
  "ttu": 45.32,
  "ttr": 12.15,
  "num_dependencies": 30
}

How It Works

  1. Package Analysis: Fetches package metadata from the registry
  2. Dependency Extraction: Extracts dependencies from the package version closest to the end date
  3. Timeline Construction: Creates a timeline of version releases for each dependency
  4. Version Resolution: Resolves dependency versions at each time interval via ecosystem resolvers
  5. Remediation Check: Uses the OSV service to determine vulnerability remediation
  6. Metric Calculation:
    • TTU: Measures time when dependency is not at the highest available version
    • TTR: Measures time when dependency has known vulnerabilities
  7. Weighting: Applies optional time-based weighting to prioritize recent periods
  8. Aggregation: Averages metrics across all dependencies

Architecture Notes

  • Ecosystem-specific resolution is encapsulated in dependency_metrics/resolvers.py.
  • OSV remediation checks are handled by dependency_metrics/osv_service.py.
  • Reporting/export helpers live in dependency_metrics/reporting.py.
  • Bulk CSV mode groups rows by (ecosystem, package_name) and processes each group sequentially while running groups in parallel to maximize cache reuse.
  • For bulk runs, a single package analysis computes metrics across multiple end dates using the latest dependency set.
  • --per-release mode uses analyze_at_release_points() which fetches per-version dependency sets and builds a shared dep-cache covering the full window; it slices that cache for each release sub-window [start_date, release_date].
  • Cache invalidation: ResolverCache (dependency_metrics/resolvers.py) persists all HTTP responses to a SQLite file at <output-dir>/cache/cache.db, keyed only by (namespace, key) — there is no TTL. This is fine for per-version data (a specific version's dependencies/metadata never changes), but package-level version-list lookups (DepsDevClient.get_package()'s depsdev_package namespace, and each resolver's fetch_package_metadata()/_get_npm_time_data() under the metadata/npm_time namespaces) cache the entire, mutable version list. Those lookups compare the requested end_date against the newest publish date already in the cached listing and transparently re-fetch whenever the cache can't yet rule out a newer release — so re-running against the same --output-dir won't silently miss versions published since the last run. The tradeoff: analyzing "as of today" re-fetches each package's version list on every run (since "today" is always newer than the last known release), which is the correct behavior — only truly historical, fixed end_date values benefit from the cache staying warm across runs.

Requirements

  • Python 3.10+
  • git (for auto-installing the patched pip on first PyPI analysis)
  • npm CLI (for npm ecosystem analysis) sudo apt install nodejs npm
  • Internet connection (for fetching package metadata and OSV data)

Note: PyPI analysis uses a patched pip fork that adds date-bounded version resolution. The tool clones it automatically to ~/.dependency_metrics/vendor/pip on first use — no manual setup required.

Development

Setup development environment

git clone https://github.com/imranur-rahman/dependency-update-metrics.git
cd dependency-update-metrics
pip install -e ".[dev]"

Run tests

pytest

Code formatting

black dependency_metrics/

Publishing to PyPI

Prepare for publishing

# Install build tools
pip install build twine

# Build distribution
python -m build

# Check distribution
twine check dist/*

Upload to PyPI

# Upload to Test PyPI first
twine upload --repository testpypi dist/*

# If all looks good, upload to PyPI
twine upload dist/*

License

MIT License - see LICENSE file for details

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Citation

If you use this tool in your research, please cite:

@software{dependency_metrics,
  author = {Rahman, Imranur},
  title = {Dependency Update Metrics},
  year = {2025},
  url = {https://github.com/imranur-rahman/dependency-update-metrics}
}

Acknowledgments

Metadata

Release files for dependency-update-metrics 0.2.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for dependency-update-metrics 0.2.1
File Size Uploaded Provenance
dependency_update_metrics-0.2.1.tar.gz 121.4 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for dependency-update-metrics 0.2.1
File Interpreter ABI Platform
dependency_update_metrics-0.2.1-py3-none-any.whl Python 3 none any Details

Total release size: 199.4 kB

Release files / dependency_update_metrics-0.2.1.tar.gz

Download URL dependency_update_metrics-0.2.1.tar.gz
Size 121.4 kB
Tags Source
SHA-256 checksum
How to use checksums
4332bf3a5389fff357a37e2e40e27d4462350ffb3438035cae94e505058578b8
BLAKE2b-256 checksum
How to use checksums
f179146436a3b9b3df060fcd70d5248bfd421931acb7aff05e50490362340cca
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Release files / dependency_update_metrics-0.2.1-py3-none-any.whl

Download URL dependency_update_metrics-0.2.1-py3-none-any.whl
Size 78.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
206a8b7b3b5bee2966b40660cc7ae7d65b7230bfeaf541f4dd0a0450ba65b740
BLAKE2b-256 checksum
How to use checksums
a7ec8643108fe9406fab45f2725fbae2c36dac6c67d84d624e805c72d11b42b3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

Security for dependency-update-metrics 0.2.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for dependency_update_metrics-0.2.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 23, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page