[Python-checkins] distutils2: basic dependency graph builder

tarek.ziade python-checkins at python.org
Mon May 31 01:56:09 CEST 2010


tarek.ziade pushed 97ff6c99d984 to distutils2:

http://hg.python.org/distutils2/rev/97ff6c99d984
changeset:   183:97ff6c99d984
user:        Josip Djolonga
date:        Sun May 30 05:09:35 2010 +0200
summary:     basic dependency graph builder
files:       src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA, src/distutils2/depgraph.py

diff --git a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
--- a/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
+++ b/src/distutils2/_backport/tests/fake_dists/grammar-1.0a4.dist-info/METADATA
@@ -1,3 +1,4 @@
 Metadata-Version: 1.2
 Name: grammar
 Version: 1.0a4
+Requires-Dist: truffles (>=1.2)
diff --git a/src/distutils2/depgraph.py b/src/distutils2/depgraph.py
new file mode 100644
--- /dev/null
+++ b/src/distutils2/depgraph.py
@@ -0,0 +1,109 @@
+"""
+A dependency graph generator. The graph is represented as an instance of
+:class:`Graph`, and DOT output is possible as well.
+"""
+
+from distutils2._backport import pkgutil
+from distutils2.errors import DistutilsError
+from distutils2.version import VersionPredicate
+
+class Graph:
+    nodes = []
+    adjacency_list = {}
+
+    def __init__(self):
+        self.nodes = []
+        self.adjacency_list = {}
+
+    def add_node(self, x):
+        self.nodes.append(x)
+        self.adjacency_list[x] = list()
+
+    def add_edge(self, x, y, label=None):
+        self.adjacency_list[x].append((y, label))
+
+    def to_dot(self, f, skip_disconnected = True):
+        """ Writes a DOT output for the graph to the provided *file* """
+
+        def dot_escape(x):
+            return x
+
+        if not isinstance(f, file):
+            raise TypeError('the argument has to be of type file')
+
+        disconnected = []
+
+        f.write("digraph dependencies {\n")
+        for dist, adjs in self.adjacency_list.iteritems():
+            if len(adjs) == 0 and not skip_disconnected:
+                disconnected.append(dist)
+            for (other, label) in adjs:
+                if not label is None:
+                    f.write('"%s" -> "%s" [label="%s"]\n' % (dist.name,
+                                                             other.name, label))
+                else:
+                    f.write('"%s" -> "%s"\n' % (dist.name, other.name))
+        if not skip_disconnected and len(disconnected) > 0:
+            f.write('subgraph disconnected {\n')
+            f.write('label = "Disconnected"\n')
+            f.write('bgcolor = red\n')
+
+            for dist in disconnected:
+                f.write('"%s"' % dist.name)
+                f.write('\n')
+            f.write('}\n')
+        f.write('}\n')
+
+def generate_graph(dists):
+    graph = Graph()
+    provided = {} # maps names to lists of (version, dist) tuples
+
+    dists = list(dists) # maybe use generator_tools to copy generators in future
+
+    # first, build the graph and find out the provides
+    for dist in dists:
+        graph.add_node(dist)
+        provides = dist.metadata['Provides-Dist'] + dist.metadata['Provides']
+
+        for p in provides:
+            comps = p.split(" ", 1)
+            name = comps[0]
+            version = None
+            if len(comps) == 2:
+                version = comps[1]
+                if len(version) < 3 or version[0] != '(' or version[-1] != ')':
+                    raise DistutilsError('Distribution %s has ill formed' \
+                                         'provides field: %s' % (dist.name, p))
+                version = version[1:-1] # trim off parenthesis
+            if not name in provided:
+                provided[name] = []
+            provided[name].append((version, dist))
+
+    print provided.keys()
+    # now make the edges
+    for dist in dists:
+        requires = dist.metadata['Requires-Dist'] + dist.metadata['Requires']
+        for req in requires:
+            predicate = VersionPredicate(req)
+            comps = req.split(" ", 1)
+            name = comps[0]
+            label = None # the label for the possible edge
+            if len(comps) == 2:
+                label = comps[1]
+
+            if not name in provided:
+                print('Requirement %s for distribution %s is missing' % \
+                      (req, dist.name))
+            else:
+                for (version, provider) in provided[name]:
+                    if predicate.match(version):
+                        graph.add_edge(dist, provider, label)
+
+    return graph
+
+if __name__ == '__main__':
+    dists = pkgutil.get_distributions(use_egg_info=True)
+    graph = generate_graph(dists)
+    f = open('output.dot', 'w')
+    graph.to_dot(f, False)
+

--
Repository URL: http://hg.python.org/distutils2


More information about the Python-checkins mailing list