From 04d0f7dc16289381910df9f2ece291238a5c9e77 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Aur=C3=A9lien=20G=C3=A2teau?= <agateau@kde.org>
Date: Wed, 5 Feb 2014 14:45:57 +0100
Subject: [PATCH 4/8] Move all of the logic from depdiagram-generate to a
 reusable module

---
 src/depdiagram-generate            | 144 ++-----------------------------
 src/kapidox/depdiagram/__init__.py |   1 +
 src/kapidox/depdiagram/generate.py | 172 +++++++++++++++++++++++++++++++++++++
 3 files changed, 178 insertions(+), 139 deletions(-)
 create mode 100644 src/kapidox/depdiagram/generate.py

diff --git a/src/depdiagram-generate b/src/depdiagram-generate
index adabe27..ad1b580 100755
--- a/src/depdiagram-generate
+++ b/src/depdiagram-generate
@@ -24,135 +24,13 @@
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 import argparse
-import itertools
 import sys
 
-
-from kapidox.depdiagram.block import Block, quote
-from kapidox.depdiagram.framework import Framework
-from kapidox.depdiagram.frameworkdb import FrameworkDb
+from kapidox import depdiagram
 
 DESCRIPTION = """\
 """
 
-ROOT_NODE_ATTRS = dict(fontsize=12, shape="box")
-
-GROUP_ATTRS = dict(style="filled", fillcolor="grey95", color="grey85")
-
-# Blocks within groups
-OTHER_ATTRS = dict(style="filled", fillcolor="cornsilk", color="black")
-
-QT_ATTRS = dict(style="filled", fillcolor="darkseagreen1", color="black")
-
-FW_ATTRS = dict(style="filled", fillcolor="azure", color="black")
-
-# Target blocks, used with --detailed
-FW_TARGET_ATTRS = dict(style="filled", fillcolor="paleturquoise")
-
-# Highlight the wanted framework, used with --framework
-WANTED_FW_ATTRS = dict(penwidth=2)
-
-class FrameworkCmp(object):
-    def __init__(self, db):
-        self.db = db
-        self.src = set(db)
-        self.dst = []
-
-    def __call__(self, fw1, fw2):
-        if self.depends_on(fw1, fw2):
-            return 1
-        if self.depends_on(fw2, fw1):
-            return -1
-        return 0
-
-    def depends_on(self, depender_fw, provider_fw):
-        for dep_target in depender_fw.get_all_target_dependencies():
-            if provider_fw.has_target(dep_target):
-                return True
-
-            try:
-                dep_fw = self.db.get_framework_for_target(dep_target)
-            except KeyError:
-                # No framework for this target, must be an external dependency,
-                # carry on
-                continue
-            if dep_fw != depender_fw and self.depends_on(dep_fw, provider_fw):
-                return True
-
-        return False
-
-
-class DotWriter(Block):
-    def __init__(self, db, out, wanted_fw=None, detailed=False):
-        Block.__init__(self, out)
-        self.db = db
-        self.detailed = detailed
-        self.wanted_fw = wanted_fw
-
-    def write(self):
-        with self.curly_block("digraph Root") as root:
-            root.write_list_attrs("node", **ROOT_NODE_ATTRS)
-
-            other_targets = self.db.find_external_targets()
-            qt_targets = set([x for x in other_targets if x.startswith("Qt")])
-            other_targets.difference_update(qt_targets)
-
-            if qt_targets:
-                with root.cluster_block("Qt", **GROUP_ATTRS) as b:
-                    b.write_list_attrs("node", **QT_ATTRS)
-                    b.write_nodes(qt_targets)
-
-            if other_targets:
-                with root.cluster_block("Others", **GROUP_ATTRS) as b:
-                    b.write_list_attrs("node", **OTHER_ATTRS)
-                    b.write_nodes(other_targets)
-
-            lst = sorted([x for x in self.db], key=lambda x: x.tier)
-            for tier, frameworks in itertools.groupby(lst, lambda x: x.tier):
-                with root.cluster_block(tier, **GROUP_ATTRS) as tier_block:
-                    tier_block.write_list_attrs("node", **FW_ATTRS)
-                    # Sort frameworks within the tier to ensure frameworks which
-                    # depend on other frameworks from that tier are listed after
-                    # their dependees.
-                    frameworks = list(frameworks)
-                    for fw in sorted(frameworks, cmp=FrameworkCmp(self.db)):
-                        if self.detailed:
-                            self.write_detailed_framework(tier_block, fw)
-                        else:
-                            self.write_framework(tier_block, fw)
-
-    def write_framework(self, tier_block, fw):
-        if fw == self.wanted_fw:
-            tier_block.write_list_attrs(quote(fw.name), **WANTED_FW_ATTRS)
-        else:
-            tier_block.write_nodes([fw.name])
-        edges = set([])
-        for target in fw.get_all_target_dependencies():
-            try:
-                target_fw = self.db.get_framework_for_target(target)
-                if fw == target_fw:
-                    continue
-                dep = target_fw.name
-            except KeyError:
-                dep = target
-            edges.add((fw.name, dep))
-        for dep_fw in fw.get_extra_frameworks():
-            edges.add((fw.name, dep_fw))
-        for edge in edges:
-            tier_block.writeln('"{}" -> "{}";'.format(*edge))
-
-    def write_detailed_framework(self, tier_block, fw):
-        with tier_block.cluster_block(fw.name, **FW_ATTRS) as fw_block:
-            if fw == self.wanted_fw:
-                fw_block.write_attrs(**WANTED_FW_ATTRS)
-            fw_block.write_list_attrs("node", **FW_TARGET_ATTRS)
-            targets = sorted(fw.get_targets())
-            fw_block.write_nodes(targets)
-            for target in targets:
-                deps = fw.get_dependencies_for_target(target)
-                for dep in sorted(deps):
-                    fw_block.writeln('"{}" -> "{}";'.format(target, dep))
-
 
 def main():
     parser = argparse.ArgumentParser(description=DESCRIPTION)
@@ -173,29 +51,17 @@ def main():
 
     args = parser.parse_args()
 
-    db = FrameworkDb()
-    db.populate(args.dot_files, with_qt=args.qt)
-
     if args.output == "-":
         out = sys.stdout
     else:
         out = open(args.output, "w")
 
-    if args.framework:
-        wanted_fw = db.find_by_name(args.framework)
-        if wanted_fw is None:
-            sys.stderr.write("No framework named {}.\n".format(args.framework))
-            return 1
-        db.remove_unused_frameworks(wanted_fw)
+    if depdiagram.generate(out, args.dot_files, framework=args.framework, with_qt=args.qt, detailed=args.detailed):
+        return 0
     else:
-        wanted_fw = None
-
-    writer = DotWriter(db, out, wanted_fw=wanted_fw, detailed=args.detailed)
-    writer.write()
-
-    return 0
-
+        return 1
 
 if __name__ == "__main__":
     sys.exit(main())
+
 # vi: ts=4 sw=4 et
diff --git a/src/kapidox/depdiagram/__init__.py b/src/kapidox/depdiagram/__init__.py
index 4e25f6d..255f093 100644
--- a/src/kapidox/depdiagram/__init__.py
+++ b/src/kapidox/depdiagram/__init__.py
@@ -1 +1,2 @@
 from prepare import *
+from generate import *
diff --git a/src/kapidox/depdiagram/generate.py b/src/kapidox/depdiagram/generate.py
new file mode 100644
index 0000000..38e1588
--- /dev/null
+++ b/src/kapidox/depdiagram/generate.py
@@ -0,0 +1,172 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# Copyright 2014  Aurélien Gâteau <agateau@kde.org>
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+#    notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+#    notice, this list of conditions and the following disclaimer in the
+#    documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+import itertools
+import sys
+
+from kapidox.depdiagram.block import Block, quote
+from kapidox.depdiagram.framework import Framework
+from kapidox.depdiagram.frameworkdb import FrameworkDb
+
+__all__ = ('generate',)
+
+ROOT_NODE_ATTRS = dict(fontsize=12, shape="box")
+
+GROUP_ATTRS = dict(style="filled", fillcolor="grey95", color="grey85")
+
+# Blocks within groups
+OTHER_ATTRS = dict(style="filled", fillcolor="cornsilk", color="black")
+
+QT_ATTRS = dict(style="filled", fillcolor="darkseagreen1", color="black")
+
+FW_ATTRS = dict(style="filled", fillcolor="azure", color="black")
+
+# Target blocks, used with --detailed
+FW_TARGET_ATTRS = dict(style="filled", fillcolor="paleturquoise")
+
+# Highlight the wanted framework, used with --framework
+WANTED_FW_ATTRS = dict(penwidth=2)
+
+class FrameworkCmp(object):
+    def __init__(self, db):
+        self.db = db
+        self.src = set(db)
+        self.dst = []
+
+    def __call__(self, fw1, fw2):
+        if self.depends_on(fw1, fw2):
+            return 1
+        if self.depends_on(fw2, fw1):
+            return -1
+        return 0
+
+    def depends_on(self, depender_fw, provider_fw):
+        for dep_target in depender_fw.get_all_target_dependencies():
+            if provider_fw.has_target(dep_target):
+                return True
+
+            try:
+                dep_fw = self.db.get_framework_for_target(dep_target)
+            except KeyError:
+                # No framework for this target, must be an external dependency,
+                # carry on
+                continue
+            if dep_fw != depender_fw and self.depends_on(dep_fw, provider_fw):
+                return True
+
+        return False
+
+
+class DotWriter(Block):
+    def __init__(self, db, out, wanted_fw=None, detailed=False):
+        Block.__init__(self, out)
+        self.db = db
+        self.detailed = detailed
+        self.wanted_fw = wanted_fw
+
+    def write(self):
+        with self.curly_block("digraph Root") as root:
+            root.write_list_attrs("node", **ROOT_NODE_ATTRS)
+
+            other_targets = self.db.find_external_targets()
+            qt_targets = set([x for x in other_targets if x.startswith("Qt")])
+            other_targets.difference_update(qt_targets)
+
+            if qt_targets:
+                with root.cluster_block("Qt", **GROUP_ATTRS) as b:
+                    b.write_list_attrs("node", **QT_ATTRS)
+                    b.write_nodes(qt_targets)
+
+            if other_targets:
+                with root.cluster_block("Others", **GROUP_ATTRS) as b:
+                    b.write_list_attrs("node", **OTHER_ATTRS)
+                    b.write_nodes(other_targets)
+
+            lst = sorted([x for x in self.db], key=lambda x: x.tier)
+            for tier, frameworks in itertools.groupby(lst, lambda x: x.tier):
+                with root.cluster_block(tier, **GROUP_ATTRS) as tier_block:
+                    tier_block.write_list_attrs("node", **FW_ATTRS)
+                    # Sort frameworks within the tier to ensure frameworks which
+                    # depend on other frameworks from that tier are listed after
+                    # their dependees.
+                    frameworks = list(frameworks)
+                    for fw in sorted(frameworks, cmp=FrameworkCmp(self.db)):
+                        if self.detailed:
+                            self.write_detailed_framework(tier_block, fw)
+                        else:
+                            self.write_framework(tier_block, fw)
+
+    def write_framework(self, tier_block, fw):
+        if fw == self.wanted_fw:
+            tier_block.write_list_attrs(quote(fw.name), **WANTED_FW_ATTRS)
+        else:
+            tier_block.write_nodes([fw.name])
+        edges = set([])
+        for target in fw.get_all_target_dependencies():
+            try:
+                target_fw = self.db.get_framework_for_target(target)
+                if fw == target_fw:
+                    continue
+                dep = target_fw.name
+            except KeyError:
+                dep = target
+            edges.add((fw.name, dep))
+        for dep_fw in fw.get_extra_frameworks():
+            edges.add((fw.name, dep_fw))
+        for edge in edges:
+            tier_block.writeln('"{}" -> "{}";'.format(*edge))
+
+    def write_detailed_framework(self, tier_block, fw):
+        with tier_block.cluster_block(fw.name, **FW_ATTRS) as fw_block:
+            if fw == self.wanted_fw:
+                fw_block.write_attrs(**WANTED_FW_ATTRS)
+            fw_block.write_list_attrs("node", **FW_TARGET_ATTRS)
+            targets = sorted(fw.get_targets())
+            fw_block.write_nodes(targets)
+            for target in targets:
+                deps = fw.get_dependencies_for_target(target)
+                for dep in sorted(deps):
+                    fw_block.writeln('"{}" -> "{}";'.format(target, dep))
+
+
+def generate(out, dot_files, framework=None, with_qt=False, detailed=False):
+    db = FrameworkDb()
+    db.populate(dot_files, with_qt=with_qt)
+
+    if framework:
+        wanted_fw = db.find_by_name(framework)
+        if wanted_fw is None:
+            sys.stderr.write("No framework named {}.\n".format(framework))
+            return False
+        db.remove_unused_frameworks(wanted_fw)
+    else:
+        wanted_fw = None
+
+    writer = DotWriter(db, out, wanted_fw=wanted_fw, detailed=detailed)
+    writer.write()
+
+    return True
+
+# vi: ts=4 sw=4 et
-- 
1.8.3.2

