Skip to main content

Explains Python dependency conflicts in plain English and suggests fixes

Project description

depwhy

Explains Python dependency conflicts in plain English and suggests ranked fixes.

The Problem

When pip, uv, or poetry fails to resolve dependencies, the error output is verbose, cryptic, and non-actionable. You're left manually cross-referencing PyPI to figure out which packages conflict and why. depwhy reads your requirements, finds every conflict, and tells you exactly what to change — with a ready-to-run pip install command.

Install

pip install depwhy

Or with uv:

uv pip install depwhy

Requires Python 3.10+.

CLI Usage

# Analyze a requirements.txt file
depwhy requirements.txt

# Analyze a pyproject.toml file
depwhy pyproject.toml

# Analyze packages installed in the current virtualenv
depwhy --env

# Output machine-readable JSON
depwhy requirements.txt --json

# Exit code only (0 = clean, 1 = conflicts) — useful in CI
depwhy requirements.txt --quiet

# Use cached metadata only (no network calls)
depwhy requirements.txt --offline

# Override target Python version for environment marker evaluation
depwhy requirements.txt --python-version 3.11

CLI Flags

Flag Description
--env Analyze installed packages in the current venv
--json Output machine-readable JSON to stdout
--quiet No output; exit code only
--offline Use disk cache only — no network calls
--python-version Override Python version for marker evaluation
--no-color Disable colored terminal output
--version Print version and exit

Exit Codes

Code Meaning
0 No conflicts
1 Conflicts found
2 Error

Example Output

$ depwhy requirements.txt

2 conflicts found

urllib3
  Problem: requests==2.28.0 requires urllib3<1.27, but you have urllib3==2.0.0 pinned
  Solution: Remove your pin — use urllib3==1.26.18 (latest compatible)
    → pip install urllib3==1.26.18
  Alternative: Upgrade requests to >=2.29.0 (loosens urllib3 upper bound)
    → pip install requests==2.29.0

certifi
  Problem: cryptography==41.0.0 requires certifi<2023.0, but pip requires certifi>=2017.4.17
  Solution: Downgrade certifi to satisfy both constraints
    → pip install certifi==2022.12.7
  Alternative: Upgrade cryptography to >=42.0.0 (drops certifi upper bound)
    → pip install cryptography==42.0.0

Library API

import depwhy

report = depwhy.analyze("requirements.txt")

if report.has_conflicts:
    for conflict in report.conflicts:
        print(conflict.package)
        print(f"  Problem:     {conflict.problem}")
        print(f"  Solution:    {conflict.solution.description}")
        print(f"               {conflict.solution.pip_command}")
        if conflict.alternative:
            print(f"  Alternative: {conflict.alternative.description}")
            print(f"               {conflict.alternative.pip_command}")
else:
    print(f"No conflicts found ({report.analyzed_packages} packages checked)")

The analyze() function accepts a path to requirements.txt or pyproject.toml, a list of requirement strings, or a pathlib.Path object:

# From a list of requirement strings
report = depwhy.analyze(["django>=4.0", "celery==5.2", "kombu==5.3"])

# With Python version and platform for environment marker evaluation
report = depwhy.analyze("requirements.txt", python_version="3.11", platform="linux")

# Offline mode (uses disk cache only — no network calls)
report = depwhy.analyze("requirements.txt", offline=True)

Return Types

analyze() returns a ConflictReport:

Field Type Description
has_conflicts bool Whether any conflicts were found
conflicts list[Conflict] All detected conflicts
warnings list[str] Narrowed-but-valid ranges (soft issues)
analyzed_packages int Total packages examined

Each Conflict contains:

Field Type Description
package str The package that cannot be resolved
problem str Plain-English one-liner describing the conflict
constraints list[Constraint] All incoming version constraints
solution FixCandidate Recommended fix
alternative FixCandidate | None Second-best fix

Each FixCandidate contains:

Field Type Description
description str Human-readable explanation of the fix
pip_command str Copy-pasteable pip install command
is_alternative bool False for the top fix, True for the fallback

How It Works

depwhy follows a six-stage pipeline: parse → fetch → graph → detect → rank → explain.

  1. Parse — reads your requirements.txt or pyproject.toml to collect every dependency and version pin.
  2. Fetch — retrieves dependency metadata from PyPI in parallel using async HTTP, caching results locally (~/.cache/depwhy/) so repeat runs are instant.
  3. Graph — builds a directed constraint graph (via networkx) connecting every package to the packages that require it and the version ranges they accept. Environment markers are evaluated so platform-specific dependencies are handled correctly.
  4. Detect — computes the intersection of all version constraints for each package using PEP 440 specifier algebra. An empty intersection means no single version can satisfy every requirement (hard conflict). A narrowed intersection triggers a soft warning.
  5. Rank — evaluates three fix strategies for each conflict — loosen a user pin, upgrade a depender, or downgrade a depender — and selects the best fix (fewest changes, smallest version delta, prefer newer) plus a fallback alternative.
  6. Explain — generates a plain-English problem description and renders the output to the terminal with color-coded labels, or as structured JSON.

Architecture

src/depwhy/
├── _analyze.py          # Core analyze() orchestrator
├── cli.py               # Typer CLI entrypoint
├── parsers/
│   ├── requirements.py  # requirements.txt parser
│   └── pyproject.py     # pyproject.toml parser
├── fetcher/
│   ├── pypi.py          # Async PyPI metadata client (httpx + semaphore)
│   └── cache.py         # Disk cache (SHA-256 keyed, TTL via mtime)
├── graph/
│   ├── builder.py       # Constraint graph construction (networkx.DiGraph)
│   └── models.py        # Frozen dataclasses: Constraint, Conflict, etc.
├── solver/
│   ├── detector.py      # Conflict detection (SpecifierSet intersection)
│   └── ranker.py        # Fix candidate ranking (3 strategies)
└── explain/
    ├── generator.py     # Template-based explanation builder
    └── formatter.py     # Rich terminal + JSON formatter

Contributing

See CONTRIBUTING.md for development setup, code quality standards, and the pull request process.

License

MIT — see LICENSE.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

depwhy-0.1.1.tar.gz (106.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

depwhy-0.1.1-py3-none-any.whl (32.8 kB view details)

Uploaded Python 3

File details

Details for the file depwhy-0.1.1.tar.gz.

File metadata

  • Download URL: depwhy-0.1.1.tar.gz
  • Upload date:
  • Size: 106.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for depwhy-0.1.1.tar.gz
Algorithm Hash digest
SHA256 7f06c14d0bfb3f8f66a980b2b59039a891072f7c9e5ed79237f0efd014d18a8c
MD5 e8aa99c1795efa18c87a262486cd3b26
BLAKE2b-256 0208e5635de4d30a8a6e5c2ff72bc8d68d1888943384af4d20faa063a4f01acf

See more details on using hashes here.

File details

Details for the file depwhy-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: depwhy-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 32.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for depwhy-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3e253e383e3fa9c722c09c20a15776876ae2434f64229eef5a0bc4bfc8e4e0cf
MD5 b3b06d0d07c14aff71dfb8d006b21799
BLAKE2b-256 65774242e1883bff5ed51233be66b94bb54c1554ffc88fef892d8f980958f795

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page