27.15.1.7. packaging.depgraph — Dependency graph builder

This module provides the means to analyse the dependencies between various distributions and to create a graph representing these dependency relationships. In this document, “distribution” refers to an instance of packaging.database.Distribution or packaging.database.EggInfoDistribution.

27.15.1.7.1. The DependencyGraph class

class packaging.depgraph.DependencyGraph

Represent a dependency graph between releases. The nodes are distribution instances; the edge model dependencies. An edge from a to b means that a depends on b.

add_distribution(distribution)

Add distribution to the graph.

add_edge(x, y, label=None)

Add an edge from distribution x to distribution y with the given label (string).

add_missing(distribution, requirement)

Add a missing requirement (string) for the given distribution.

repr_node(dist, level=1)

Print a subgraph starting from dist. level gives the depth of the subgraph.

Direct access to the graph nodes and edges is provided through these attributes:

adjacency_list

Dictionary mapping distributions to a list of (other, label) tuples where other is a distribution and the edge is labeled with label (i.e. the version specifier, if such was provided).

reverse_list

Dictionary mapping distributions to a list of predecessors. This allows efficient traversal.

missing

Dictionary mapping distributions to a list of requirements that were not provided by any distribution.

27.15.1.7.2. Auxiliary functions

packaging.depgraph.dependent_dists(dists, dist)

Recursively generate a list of distributions from dists that are dependent on dist.

packaging.depgraph.generate_graph(dists)

Generate a DependencyGraph from the given list of distributions.

packaging.depgraph.graph_to_dot(graph, f, skip_disconnected=True)

Write a DOT output for the graph to the file-like object f.

If skip_disconnected is true, all distributions that are not dependent on any other distribution are skipped.

27.15.1.7.3. Example Usage

27.15.1.7.3.1. Depict all dependenciess in the system

First, we shall generate a graph of all the distributions on the system and then create an image out of it using the tools provided by Graphviz:

from packaging.database import get_distributions
from packaging.depgraph import generate_graph

dists = list(get_distributions())
graph = generate_graph(dists)

It would be interesting to print out the missing requirements. This can be done as follows:

for dist, reqs in graph.missing.items():
    if reqs:
        reqs = ' ,'.join(repr(req) for req in reqs)
        print('Missing dependencies for %r: %s' % (dist.name, reqs))

Example output is:

Missing dependencies for 'TurboCheetah': 'Cheetah'
Missing dependencies for 'TurboGears': 'ConfigObj', 'DecoratorTools', 'RuleDispatch'
Missing dependencies for 'jockey': 'PyKDE4.kdecore', 'PyKDE4.kdeui', 'PyQt4.QtCore', 'PyQt4.QtGui'
Missing dependencies for 'TurboKid': 'kid'
Missing dependencies for 'TurboJson: 'DecoratorTools', 'RuleDispatch'

Now, we proceed with generating a graphical representation of the graph. First we write it to a file, and then we generate a PNG image using the dot command-line tool:

from packaging.depgraph import graph_to_dot
with open('output.dot', 'w') as f:
   # only show the interesting distributions, skipping the disconnected ones
   graph_to_dot(graph, f, skip_disconnected=True)

We can create the final picture using:

$ dot -Tpng output.dot > output.png

An example result is:

Example PNG output from packaging.depgraph and dot

If you want to include egg distributions as well, then the code requires only one change, namely the line:

dists = list(packaging.database.get_distributions())

has to be replaced with:

dists = list(packaging.database.get_distributions(use_egg_info=True))

On many platforms, a richer graph is obtained because at the moment most distributions are provided in the egg rather than the new standard .dist-info format.

27.15.1.7.3.2. List all dependent distributions

We will list all distributions that are dependent on some given distibution. This time, egg distributions will be considered as well:

import sys
from packaging.database import get_distribution, get_distributions
from packaging.depgraph import dependent_dists

dists = list(get_distributions(use_egg_info=True))
dist = get_distribution('bacon', use_egg_info=True)
if dist is None:
    sys.exit('No such distribution in the system')

deps = dependent_dists(dists, dist)
deps = ', '.join(repr(x.name) for x in deps)
print('Distributions depending on %r: %s' % (dist.name, deps))

And this is example output:

Distributions depending on 'bacon': 'towel-stuff', 'choxie', 'grammar'