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 (when published)
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 .
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.
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, orcrates.io) [Required unless--input-csv]--package: Package name to analyze [Required unless--input-csv]--input-csv: CSV file with columnsecosystem, package_name, optionalstart_date, and optionalend_date. Ifend_dateis omitted,--end-dateis 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/update the OSV vulnerability database--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)
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:
- JSON results file: Contains TTU, TTR, and metadata
- OSV data (with
--get-osv): CSV file with vulnerability information - Excel worksheets (with
--get-worksheets): Detailed analysis for each dependency - Bulk CSV results (with
--input-csv): Summary CSV, plus an optional dependency details CSV with--write-dependency-details - Per-release results (with
--per-release): One row per package release withpackage_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
- Package Analysis: Fetches package metadata from the registry
- Dependency Extraction: Extracts dependencies from the package version closest to the end date
- Timeline Construction: Creates a timeline of version releases for each dependency
- Version Resolution: Resolves dependency versions at each time interval via ecosystem resolvers
- Remediation Check: Uses the OSV service to determine vulnerability remediation
- Metric Calculation:
- TTU: Measures time when dependency is not at the highest available version
- TTR: Measures time when dependency has known vulnerabilities
- Weighting: Applies optional time-based weighting to prioritize recent periods
- 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-releasemode usesanalyze_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()'sdepsdev_packagenamespace, and each resolver'sfetch_package_metadata()/_get_npm_time_data()under themetadata/npm_timenamespaces) cache the entire, mutable version list. Those lookups compare the requestedend_dateagainst 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-dirwon'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, fixedend_datevalues 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/pipon 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
- Uses data from Open Source Vulnerabilities (OSV)
- Package metadata from npm Registry and PyPI
Metadata
Release files for dependency-update-metrics 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| dependency_update_metrics-0.1.0.tar.gz | 98.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| dependency_update_metrics-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 166.1 kB
Release files / dependency_update_metrics-0.1.0.tar.gz
| Download URL | dependency_update_metrics-0.1.0.tar.gz |
|---|---|
| Size | 98.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
797b3ff1efeffd19d7f11243e0b9777bef50c03daca3c3cbd752b4cc05d76172
|
|
BLAKE2b-256 checksum How to use checksums |
1a0682f4b3f7dfbec216135dc2a79d8928448795df5e578359034a9717e10ab2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 18, 2026.
Transparency logRelease files / dependency_update_metrics-0.1.0-py3-none-any.whl
| Download URL | dependency_update_metrics-0.1.0-py3-none-any.whl |
|---|---|
| Size | 67.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
af4d639c50a875c7092f16796ba052abc52f866c2bc33a46bf6d79510212c384
|
|
BLAKE2b-256 checksum How to use checksums |
08384ebe880db1a78b3de921f7db870a350d04013b35f7b01caf106176fbdebe
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 18, 2026.
Transparency log