Understanding Multiple Package Versions in a Graph

When building complex Python projects, you may encounter situations where multiple versions of the same package appear in your dependency graph. The fromager graph explain-duplicates command helps you understand why this happens and whether it represents a problem.

Basic Usage

To analyze multiple versions in your graph:

fromager graph explain-duplicates e2e/build-parallel/graph.json

This command will scan the entire graph and report on any packages that have multiple versions present.

Understanding the Output

The command output shows:

  1. Package name: The name of the package with multiple versions

  2. Available versions: All versions found in the graph

  3. Requirements analysis: Which packages require which versions

  4. Compatibility assessment: Whether a single version can satisfy all requirements

Example Output

Here’s an example of what you might see:

setuptools
  80.8.0
    setuptools>=61.2 matches ['80.8.0']
      keyring==25.6.0
    setuptools>=61 matches ['80.8.0']
      setuptools-scm==8.3.1
    setuptools matches ['80.8.0']
      setuptools-scm==8.3.1
  * setuptools==80.8.0 usable by all consumers

This output tells us:

  • setuptools version 80.8.0 is present in the graph

  • Three different requirement specifications exist for setuptools

  • All requirements can be satisfied by version 80.8.0

  • No version conflicts exist

Interpreting Results

Good Case: Single Compatible Version

When you see output like:

* package-name==1.2.3 usable by all consumers

This means all packages that depend on this package can use the same version. This is the ideal situation.

Problem Case: Version Conflicts

When you see output like:

* No single version of package-name meets all requirements

This indicates a dependency conflict where different packages require incompatible versions of the same dependency.

Common Scenarios

Build vs Runtime Dependencies

Sometimes you’ll see the same package required at different versions for build-time and runtime:

setuptools
  45.0.0
    setuptools<60 matches ['45.0.0']
      some-old-package==1.0.0
  65.0.0
    setuptools>=60 matches ['65.0.0']
      modern-package==2.0.0
* No single version of setuptools meets all requirements

In this case, you might need to update the older package or use package overrides.

Transitive Dependencies

Multiple versions can appear when different top-level packages pull in different versions of the same transitive dependency.

Resolution Strategies

When you find version conflicts:

  1. Update packages: Try updating packages to newer versions that have compatible requirements

  2. Use constraints: Create a constraints file to pin specific versions

  3. Package overrides: Use fromager’s override system to force specific versions

  4. Remove conflicting packages: Consider if all dependencies are actually needed

Example Investigation Workflow

# 1. Check for duplicates
fromager graph explain-duplicates e2e/build-parallel/graph.json

# 2. If conflicts found, investigate why specific packages are included
fromager graph why e2e/build-parallel/graph.json problematic-package

# 3. Check the full dependency chain
fromager graph why e2e/build-parallel/graph.json problematic-package --depth -1

# 4. Visualize to better understand the relationships
fromager graph to-dot e2e/build-parallel/graph.json --output graph.dot
dot -Tpng graph.dot -o dependency-analysis.png

This workflow helps you:

  1. Identify which packages have version conflicts

  2. Understand why conflicting packages are included

  3. See the complete dependency chain causing conflicts

  4. Visualize the relationships for better analysis

Best Practices

  • Run explain-duplicates regularly during development to catch conflicts early

  • Pay attention to build-system vs install requirements, as they often have different version constraints

  • Use the why command to understand the source of unexpected version requirements

  • Consider using dependency scanning tools in your CI/CD pipeline to detect new conflicts