Python project dependency analyzer
Project description
pyproj_dep_analyze
Parses pyproject.toml to generate actionable dependency data for security audits, update automation, and LLM-powered code review.
Why pyproj_dep_analyze?
AI-assisted "vibe coding" creates apps fast, but dependencies are often unvetted.
When developers build applications rapidly with AI assistance, the focus is on functionality, not supply chain security. Dependencies get added without scrutiny, creating blind spots that attackers exploit:
| Attack Vector | Description |
|---|---|
| Typosquatting | Malicious packages with names similar to popular ones (reqeusts vs requests) |
| Dependency Confusion | Private package names hijacked on public PyPI |
| Malicious Updates | Legitimate packages compromised via maintainer account takeover |
| Abandoned Package Takeover | Unmaintained packages acquired by bad actors |
| Protestware / Sabotage | Maintainers intentionally breaking their own packages |
| Hidden Malware | Obfuscated code in install scripts or deep dependencies |
pyproj_dep_analyze provides visibility into your dependency landscape, generating structured data that feeds into security workflows:
pyproj_dep_analyze → pyproj_dep_security → pyproj_dep_update
(analyze) (scan & audit) (remediate)
pyproj_dep_analyze- Extracts and enriches dependency metadata frompyproject.tomlpyproj_dep_security- Scans installed environments for vulnerabilities and supply chain riskspyproj_dep_update- Applies fixes by updatingpyproject.tomlbased on security findings
Know your dependencies before they become your vulnerabilities.
Table of Contents
- Why pyproj_dep_analyze?
- Overview
- Scope & Boundaries
- Installation
- Quick Start
- CLI Reference
- Python API
- Data Models
- Configuration
- Output Examples
- Further Documentation
Overview
pyproj_dep_analyze analyzes Python project dependencies declared in pyproject.toml files:
- Parses dependencies from PEP 621, Poetry, PDM, and Hatch formats
- Checks for newer versions on PyPI and GitHub
- Enriches with metadata: license, stars, release dates, download stats
- Outputs structured JSON with human/LLM-readable recommendations
Scope & Boundaries
What pyproj_dep_analyze Does
| Capability | Description |
|---|---|
| Parse pyproject.toml | Extract dependencies from PEP 621, Poetry, PDM, Hatch formats |
| Check for updates | Query PyPI/GitHub for latest compatible versions |
| Enrich with metadata | License, stars, forks, release dates, maintainers |
| Version metrics | Release frequency, project age, abandonment indicators |
| Download statistics | Popularity metrics from pypistats.org |
| Index detection | Identify which package index serves each dependency |
| Direct dependencies | Extract immediate dependencies from requires_dist |
What Belongs in pyproj_dep_security (Separate Project)
| Capability | Reason |
|---|---|
| Full transitive dependency tree | Requires installed environment (pipdeptree) |
| Vulnerability scanning | Security-specific (OSV, Safety DB, Snyk integration) |
| License compliance checking | Policy enforcement, not metadata |
| Dependency confusion detection | Active security scanning |
| Supply chain risk scoring | Requires vulnerability data + heuristics |
| Typosquatting detection | Requires name similarity analysis |
What Belongs in pyproj_dep_update (Separate Project)
| Capability | Reason |
|---|---|
| Update pyproject.toml | Modifies project files (write operation) |
| Pin indirect dependencies | Add transitive deps with CVEs to explicit deps |
| CVE-driven version bumps | Requires vulnerability scan results (Bandit/OSV) |
| Generate constraints.txt | Create pip constraints for indirect deps |
| Batch update operations | Update all / security-only / major/minor/patch |
| Lock file regeneration | Update poetry.lock, pdm.lock, uv.lock after changes |
| Update local libraries | Bump versions of local/workspace dependencies |
Design Principle:
pyproj_dep_analyzeanalyzes declared dependencies from project filespyproj_dep_securityanalyzes installed environments (in a venv) for security issuespyproj_dep_updatemodifies pyproject.toml based on analysis and security scan results
Installation
Via UV (Recommended)
uv pip install pyproj_dep_analyze
Via pip
pip install pyproj_dep_analyze
Requirements: Python 3.10+
Both pyproj_dep_analyze and pyproj-dep-analyze commands are available after installation.
Quick Start
# Basic analysis
pyproj-dep-analyze analyze
# Enriched analysis with full metadata
pyproj-dep-analyze analyze-enriched
# View configuration
pyproj-dep-analyze config
# Show package info
pyproj-dep-analyze info
CLI Reference
Global Options
| Option | Description | Default |
|---|---|---|
--traceback / --no-traceback |
Show full Python traceback on errors | --no-traceback |
-h, --help |
Show help message | - |
--version |
Show version and exit | - |
analyze - Analyze Dependencies
Analyze pyproject.toml and determine outdated dependencies for each Python version.
pyproj-dep-analyze analyze [PYPROJECT_PATH] [OPTIONS]
Arguments
| Argument | Description | Default |
|---|---|---|
PYPROJECT_PATH |
Path to pyproject.toml file | pyproject.toml |
Options
| Option | Description | Env Variable | Default |
|---|---|---|---|
-o, --output |
Output file path | - | outdated.json |
--github-token |
GitHub API token | GITHUB_TOKEN, PYPROJ_DEP_ANALYZE_GITHUB_TOKEN |
None |
--timeout |
Request timeout (seconds) | PYPROJ_DEP_ANALYZE_TIMEOUT |
30.0 |
--concurrency |
Max concurrent API requests | PYPROJ_DEP_ANALYZE_CONCURRENCY |
10 |
--format |
Output format | - | table |
Format Options:
| Format | Description |
|---|---|
table |
Summary statistics + lists of updates and manual checks |
summary |
Only summary statistics |
json |
Full analysis as JSON to stdout |
Examples
# Analyze current directory
pyproj-dep-analyze analyze
# Analyze specific file
pyproj-dep-analyze analyze path/to/pyproject.toml
# Custom output file
pyproj-dep-analyze analyze -o results.json
# With GitHub token (for better rate limits)
pyproj-dep-analyze analyze --github-token ghp_xxxxx
GITHUB_TOKEN=ghp_xxxxx pyproj-dep-analyze analyze
# Output formats
pyproj-dep-analyze analyze --format summary
pyproj-dep-analyze analyze --format json
# Custom timeout and concurrency
pyproj-dep-analyze analyze --timeout 60 --concurrency 5
analyze-enriched - Enriched Analysis
Analyze with full metadata enrichment including PyPI info, repository data, and dependency graphs.
pyproj-dep-analyze analyze-enriched [PYPROJECT_PATH] [OPTIONS]
Arguments
| Argument | Description | Default |
|---|---|---|
PYPROJECT_PATH |
Path to pyproject.toml file | pyproject.toml |
Options
| Option | Description | Env Variable | Default |
|---|---|---|---|
-o, --output |
Output file path | - | deps_enriched.json |
--github-token |
GitHub API token | GITHUB_TOKEN, PYPROJ_DEP_ANALYZE_GITHUB_TOKEN |
None |
--timeout |
Request timeout (seconds) | PYPROJ_DEP_ANALYZE_TIMEOUT |
30.0 |
--concurrency |
Max concurrent API requests | PYPROJ_DEP_ANALYZE_CONCURRENCY |
10 |
Examples
# Enriched analysis
pyproj-dep-analyze analyze-enriched
# Custom output file
pyproj-dep-analyze analyze-enriched -o analysis.json
# With GitHub token for repository metadata
GITHUB_TOKEN=ghp_xxx pyproj-dep-analyze analyze-enriched
config - View Configuration
Display the current merged configuration from all sources.
pyproj-dep-analyze config [OPTIONS]
Options
| Option | Description | Values | Default |
|---|---|---|---|
--format |
Output format | human, json |
human |
--section |
Show specific section only | e.g., analyzer |
None |
Examples
pyproj-dep-analyze config
pyproj-dep-analyze config --format json
pyproj-dep-analyze config --section analyzer
config-deploy - Deploy Configuration
Deploy default configuration files to system or user directories.
pyproj-dep-analyze config-deploy --target TARGET [OPTIONS]
Options
| Option | Description | Values | Default |
|---|---|---|---|
--target |
Target layer(s) - can be repeated | app, host, user |
Required |
--force |
Overwrite existing files | Flag | False |
Target Locations
| Target | Linux | macOS / Windows |
|---|---|---|
user |
~/.config/pyproj-dep-analyze/config.toml |
Platform-specific user config |
app |
/etc/xdg/pyproj-dep-analyze/config.toml |
Platform-specific system config |
host |
/etc/pyproj-dep-analyze/hosts/{hostname}.toml |
Same as app |
Examples
pyproj-dep-analyze config-deploy --target user
pyproj-dep-analyze config-deploy --target user --force
sudo pyproj-dep-analyze config-deploy --target app
info - Package Information
Display package metadata including version and installation details.
pyproj-dep-analyze info
hello / fail - Test Commands
Commands for testing CLI success and failure paths.
pyproj-dep-analyze hello # Success path
pyproj-dep-analyze fail # Failure path
pyproj-dep-analyze --traceback fail # With full traceback
Python API
Main Functions
analyze_pyproject()
Analyze a pyproject.toml file and return outdated entries.
from pyproj_dep_analyze import analyze_pyproject, OutdatedEntry, Action
entries: list[OutdatedEntry] = analyze_pyproject(
"pyproject.toml", # Path to pyproject.toml (required)
github_token=None, # GitHub API token (default: None)
timeout=30.0, # Request timeout in seconds (default: 30.0)
concurrency=10, # Max concurrent requests (default: 10)
)
# Filter results
updates = [e for e in entries if e.action == Action.UPDATE]
for entry in updates:
print(f"{entry.package}: {entry.current_version} -> {entry.latest_version}")
run_enriched_analysis()
Analyze with full metadata enrichment.
from pyproj_dep_analyze import run_enriched_analysis, write_enriched_json
result = run_enriched_analysis(
"pyproject.toml", # Path to pyproject.toml (required)
github_token=None, # GitHub API token (default: None)
timeout=30.0, # Request timeout in seconds (default: 30.0)
concurrency=10, # Max concurrent requests (default: 10)
)
# Access results
print(f"Total: {result.summary.total_packages}")
print(f"Updates: {result.summary.updates_available}")
for pkg in result.packages:
print(f"{pkg.name}: {pkg.action.value}")
if pkg.pypi_metadata:
print(f" License: {pkg.pypi_metadata.license}")
if pkg.repo_metadata:
print(f" Stars: {pkg.repo_metadata.stars}")
# Save to file
write_enriched_json(result, "analysis.json")
write_outdated_json() / write_enriched_json()
Write analysis results to JSON files.
from pyproj_dep_analyze import (
analyze_pyproject,
run_enriched_analysis,
write_outdated_json,
write_enriched_json,
)
# Basic analysis
entries = analyze_pyproject("pyproject.toml")
write_outdated_json(entries, "outdated.json")
# Enriched analysis
result = run_enriched_analysis("pyproject.toml")
write_enriched_json(result, "deps_enriched.json")
Analyzer Class
For more control, use the Analyzer class directly.
from pyproj_dep_analyze import Analyzer
from pathlib import Path
analyzer = Analyzer(
github_token="ghp_xxxxx", # GitHub API token (default: None)
timeout=60.0, # Request timeout in seconds (default: 30.0)
concurrency=20, # Max concurrent requests (default: 10)
)
# Basic analysis
result = analyzer.analyze(Path("pyproject.toml"))
for entry in result.entries:
print(f"{entry.package}: {entry.action.value}")
# Enriched analysis
enriched = analyzer.analyze_enriched(Path("pyproject.toml"))
Index Resolution
Detect and resolve package indexes.
from pyproj_dep_analyze import (
IndexResolver,
detect_configured_indexes,
identify_index,
)
# Detect all configured indexes
indexes = detect_configured_indexes()
for idx in indexes:
print(f"{idx.url} ({idx.index_type.value}, private={idx.is_private})")
# Identify index type from URL
info = identify_index("https://pypi.org/simple")
print(info.index_type) # IndexType.PYPI
# Resolve which index serves a package (async)
resolver = IndexResolver(indexes=indexes, timeout=30.0)
index_info = await resolver.resolve("requests")
Repository Resolution
Resolve repository metadata from URLs.
from pyproj_dep_analyze import (
RepoResolver,
detect_repo_url,
parse_repo_url,
PyPIUrlMetadata,
)
# Parse repository URL
parsed = parse_repo_url("https://github.com/psf/requests")
print(f"{parsed.owner}/{parsed.name}") # psf/requests
# Detect repo URL from PyPI metadata
urls = PyPIUrlMetadata(project_urls={"Source": "https://github.com/psf/requests"})
repo_url = detect_repo_url(urls)
# Fetch repository metadata (async)
resolver = RepoResolver(github_token="ghp_xxx", timeout=30.0)
metadata = await resolver.resolve("psf", "requests")
print(f"Stars: {metadata.stars}")
Download Statistics
Fetch download statistics from pypistats.org.
from pyproj_dep_analyze import StatsResolver
resolver = StatsResolver(timeout=15.0)
# Fetch stats for a single package (async)
stats = await resolver.fetch_stats_async("requests")
if stats:
print(f"Last month: {stats.last_month_downloads}")
print(f"Last week: {stats.last_week_downloads}")
# Fetch stats for multiple packages (async)
results = await resolver.fetch_many_async(
["requests", "httpx", "pydantic"],
concurrency=5,
)
Data Models
Enums
Action
from pyproj_dep_analyze import Action
Action.UPDATE # "update" - newer version exists
Action.DELETE # "delete" - remove for this Python version
Action.NONE # "none" - up to date
Action.CHECK_MANUALLY # "check manually" - needs manual verification
Analysis Results
OutdatedEntry
Basic analysis entry for a single dependency.
| Field | Type | Description |
|---|---|---|
package |
str |
Package name |
python_version |
str |
Python version (e.g., "3.11") |
current_version |
str | None |
Currently specified version |
latest_version |
str | None |
Latest available version |
action |
Action |
Recommended action |
note |
str |
Human/LLM-readable explanation |
AnalysisResult
Complete result from basic analysis.
| Field | Type | Description |
|---|---|---|
entries |
list[OutdatedEntry] |
All analysis entries |
python_versions |
list[str] |
Python versions analyzed |
total_dependencies |
int |
Total unique dependencies |
update_count |
int |
Dependencies needing updates |
delete_count |
int |
Dependencies to delete |
check_manually_count |
int |
Needs manual check |
EnrichedAnalysisResult
Complete result from enriched analysis.
| Field | Type | Description |
|---|---|---|
analyzed_at |
str |
ISO timestamp |
pyproject_path |
str |
Analyzed file path |
python_versions |
list[str] |
Python versions |
indexes_configured |
list[IndexInfo] |
Package indexes |
packages |
list[EnrichedEntry] |
Enriched entries |
dependency_graph |
dict[str, list[str]] |
Dependencies |
summary |
EnrichedSummary |
Statistics |
EnrichedEntry
Enriched package entry with full metadata.
| Field | Type | Description |
|---|---|---|
name |
str |
Package name |
requested_version |
str | None |
Version constraint |
resolved_version |
str | None |
Resolved version |
latest_version |
str | None |
Latest version |
action |
Action |
Recommended action |
note |
str |
Human/LLM-readable explanation |
source |
str |
Where declared |
index_info |
IndexInfo | None |
Package index |
python_compatibility |
dict[str, CompatibilityStatus] |
Per-version compatibility |
pypi_metadata |
PyPIMetadata | None |
PyPI data |
repo_metadata |
RepoMetadata | None |
Repository data |
direct_dependencies |
list[str] |
Runtime dependencies only |
optional_dependencies |
dict[str, list[str]] |
Optional deps grouped by extra (dev, test, docs) |
required_by |
list[str] |
Reverse dependencies |
Metadata Models
PyPIMetadata
| Field | Type | Description |
|---|---|---|
summary |
str | None |
One-line description |
license |
str | None |
SPDX license |
home_page |
str | None |
Project URL |
project_urls |
dict[str, str] |
Labeled URLs |
author |
str | None |
Author name |
author_email |
str | None |
Author email |
maintainer |
str | None |
Maintainer name |
maintainer_email |
str | None |
Maintainer email |
available_versions |
list[str] |
All versions |
first_release_date |
str | None |
First release ISO date |
latest_release_date |
str | None |
Latest release ISO date |
requires_python |
str | None |
Python constraint |
requires_dist |
list[str] |
Dependency specs |
version_metrics |
VersionMetrics | None |
Release pattern metrics |
download_stats |
DownloadStats | None |
Download statistics |
VersionMetrics
Computed metrics from version history for quality assessment.
| Field | Type | Description |
|---|---|---|
release_count |
int |
Total number of releases |
latest_release_age_days |
int | None |
Days since latest release |
first_release_age_days |
int | None |
Project age in days |
avg_days_between_releases |
float | None |
Average release frequency |
min_days_between_releases |
int | None |
Shortest gap (detect rapid releases) |
max_days_between_releases |
int | None |
Longest gap (detect abandonment) |
releases_last_year |
int |
Recent activity indicator |
release_dates |
list[str] |
All release timestamps |
DownloadStats
Download statistics from pypistats.org.
| Field | Type | Description |
|---|---|---|
total_downloads |
int | None |
Lifetime downloads |
last_month_downloads |
int | None |
Last 30 days |
last_week_downloads |
int | None |
Last 7 days |
last_day_downloads |
int | None |
Last 24 hours |
fetched_at |
str | None |
When stats were retrieved |
RepoMetadata
| Field | Type | Description |
|---|---|---|
repo_type |
RepoType |
github, gitlab, etc. |
url |
str | None |
Repository URL |
owner |
str | None |
Owner/organization |
name |
str | None |
Repository name |
stars |
int | None |
Star count |
forks |
int | None |
Fork count |
open_issues |
int | None |
Open issue count |
default_branch |
str | None |
Default branch |
last_commit_date |
str | None |
Last commit ISO date |
created_at |
str | None |
Creation ISO date |
description |
str | None |
Repository description |
IndexInfo
| Field | Type | Description |
|---|---|---|
url |
str |
Index URL |
index_type |
IndexType |
pypi, testpypi, artifactory, etc. |
is_private |
bool |
Whether private/internal |
Utility Models
DependencyInfo
Parsed dependency information.
| Field | Type | Description |
|---|---|---|
name |
str |
Normalized package name |
raw_spec |
str |
Original specification |
version_constraints |
str |
Version constraints |
python_markers |
str | None |
Python version markers |
extras |
list[str] |
Requested extras |
source |
str |
Source location |
is_git_dependency |
bool |
Is git dependency |
git_url |
str | None |
Git URL |
git_ref |
str | None |
Git ref (tag/branch/commit) |
PythonVersion
from pyproj_dep_analyze import PythonVersion
pv = PythonVersion.from_string("3.11")
pv.major # 3
pv.minor # 11
str(pv) # "3.11"
# Comparisons
pv < PythonVersion(3, 12) # True
pv >= PythonVersion(3, 10) # True
Configuration
Configuration Precedence (lowest to highest)
- Default config (bundled)
- Application config (
/etc/xdg/pyproj-dep-analyze/config.toml) - Host config (
/etc/pyproj-dep-analyze/hosts/{hostname}.toml) - User config (
~/.config/pyproj-dep-analyze/config.toml) .envfiles- Environment variables (
PYPROJ_DEP_ANALYZE_*) - CLI options
Analyzer Settings
| Setting | Type | Default | Environment Variable |
|---|---|---|---|
github_token |
string | "" |
PYPROJ_DEP_ANALYZE_GITHUB_TOKEN |
timeout |
float | 30.0 |
PYPROJ_DEP_ANALYZE_TIMEOUT |
concurrency |
integer | 10 |
PYPROJ_DEP_ANALYZE_CONCURRENCY |
GitHub Token
| Mode | Rate Limit | Notes |
|---|---|---|
| Unauthenticated | 60 requests/hour | Easily exhausted |
| Authenticated | 5,000 requests/hour | Recommended for regular use |
Sample Config File
# ~/.config/pyproj-dep-analyze/config.toml
[analyzer]
timeout = 30.0
concurrency = 10
# github_token = "" # Use env var instead
Sample .env File
PYPROJ_DEP_ANALYZE_GITHUB_TOKEN=ghp_xxxxx
PYPROJ_DEP_ANALYZE_TIMEOUT=60.0
PYPROJ_DEP_ANALYZE_CONCURRENCY=20
LOG_CONSOLE_LEVEL=DEBUG
Output Examples
outdated.json
[
{
"package": "requests",
"python_version": "3.11",
"current_version": "2.28.0",
"latest_version": "2.32.0",
"action": "update",
"note": "Package 'requests' can be updated from 2.28.0 to 2.32.0."
},
{
"package": "tomli",
"python_version": "3.11",
"current_version": "2.0.0",
"latest_version": null,
"action": "delete",
"note": "Package 'tomli' has a Python version marker that excludes Python 3.11."
}
]
deps_enriched.json
{
"analyzed_at": "2025-12-04T10:30:00Z",
"pyproject_path": "pyproject.toml",
"python_versions": ["3.11", "3.12", "3.13"],
"indexes_configured": [
{"url": "https://pypi.org/simple", "index_type": "pypi", "is_private": false}
],
"summary": {
"total_packages": 25,
"updates_available": 5,
"up_to_date": 18,
"check_manually": 2,
"from_pypi": 23,
"from_private_index": 2
},
"packages": [
{
"name": "requests",
"requested_version": ">=2.28.0",
"latest_version": "2.32.0",
"action": "update",
"pypi_metadata": {
"license": "Apache-2.0",
"latest_release_date": "2024-05-29T...",
"version_metrics": {
"release_count": 150,
"latest_release_age_days": 180,
"releases_last_year": 5
}
},
"repo_metadata": {
"stars": 52345,
"forks": 9234
}
}
]
}
Further Documentation
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 pyproj_dep_analyze-4.0.1.tar.gz.
File metadata
- Download URL: pyproj_dep_analyze-4.0.1.tar.gz
- Upload date:
- Size: 4.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86126a526f642c11dc9c313dd39c91d67c888e4a3855318980153a815cf3765d
|
|
| MD5 |
320a2ffd50562527179a3084977d9510
|
|
| BLAKE2b-256 |
b4c9c6307746f92523925a6a89e1beef95f11dd19c1430493761167209ff5dc2
|
File details
Details for the file pyproj_dep_analyze-4.0.1-py3-none-any.whl.
File metadata
- Download URL: pyproj_dep_analyze-4.0.1-py3-none-any.whl
- Upload date:
- Size: 82.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7a049db84883d9a5aed310f9476fe366aa6c46c54f6daad9d4e48f31e6a972e1
|
|
| MD5 |
fcdd54d26c6a451f41b515b28d8d595f
|
|
| BLAKE2b-256 |
04cee8cba279b9e7e8e7f405989f1619ce9b0d362f375d3aa2841426abf5b363
|