Merge lp:~dotdepends-team/dotdepends/prune into lp:dotdepends

Proposed by Kyle Nitzsche
Status: Merged
Approved by: Mike Carifio
Approved revision: 89
Merged at revision: 84
Proposed branch: lp:~dotdepends-team/dotdepends/prune
Merge into: lp:dotdepends
Diff against target: 333 lines (+286/-1)
6 files modified
Dotdepends/Dot.py (+20/-0)
data/dotdepends-prune.1 (+74/-0)
debian/changelog (+8/-0)
debian/manpages (+1/-0)
usr/bin/dotdepends (+1/-1)
usr/bin/dotdepends-prune (+182/-0)
To merge this branch: bzr merge lp:~dotdepends-team/dotdepends/prune
Reviewer Review Type Date Requested Status
Mike Carifio Approve
Review via email: mp+123330@code.launchpad.net

Description of the change

add dotdepends-prune and manpage.

dotdepends-prune tool provides a quick path for generating reverse dependency dot files and png files by pruning a (possibly very large), pre-existing dotdepends dot file (one for ubuntu-desktop, for example) to:
* only include the parts of the tree that lead to a "bottom" pkg (this creates the reverse dependency tree)
* and then optionally "top pruning" the tree to exclude bits that don't involve the 'top' package.

Bottom line: fast png graphs of highly targeted rdepends trees.

To post a comment you must log in.
lp:~dotdepends-team/dotdepends/prune updated
89. By Kyle Nitzsche

usr/bin/dotdepends-prune:
* add copyright header
* fix typo ('reversly')

Revision history for this message
Mike Carifio (carifio) wrote :

gtg

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Dotdepends/Dot.py'
2--- Dotdepends/Dot.py 2012-07-11 20:17:42 +0000
3+++ Dotdepends/Dot.py 2012-09-07 18:40:25 +0000
4@@ -71,6 +71,26 @@
5 self.rnodes = self.get_rnodes()
6 self.footer = "}\n}"
7 self.shape = shape
8+ self.nodePkg, self.pkgNode = self.getNodeNames(self.nodes)
9+
10+ def getNodeNames(self, nodes):
11+ """Return two dicts used to convert between pkg names and dot node
12+ names."""
13+
14+ nodePkg = dict()
15+ pkgNode = dict()
16+ for node in nodes:
17+ style = nodes[node]
18+ label = style[style.find('label="'):]
19+ pkgName = label[label.find('"')+1:].rstrip('"')
20+ if len(pkgName) > 0:
21+ pkg = pkgName
22+ else:
23+ pkg = node
24+ nodePkg[node] = pkg
25+ pkgNode[pkg] = node
26+
27+ return nodePkg, pkgNode
28
29 def get_root(self):
30 line = self.dot[0]
31
32=== added file 'data/dotdepends-prune.1'
33--- data/dotdepends-prune.1 1970-01-01 00:00:00 +0000
34+++ data/dotdepends-prune.1 2012-09-07 18:40:25 +0000
35@@ -0,0 +1,74 @@
36+.TH dotdepends-prune 1 "5 Sep 2012" "version 0.1"
37+.SH NAME
38+\fBdotdepends-prune\fP
39+.SH SYNOPSIS
40+.B dotdepends-prune [-t TOPPACKAGE] -b BOTTOMPACKAGE FILE
41+.br
42+.SH DESCRIPTION
43+Prunes a dependency tree in a dot file produced by dotdepends to include only
44+the dependency chains that lead to a specified package (the 'bottom' package).
45+This tree represents the reverse dependencies of the 'bottom' package within
46+the scope of the initial dot file. For example, you can create a dot file for
47+ubuntu-desktop, then, with dotdepends-prune, create a new dot file that only
48+includes the 'rdepends' tree for a particular package.
49+.SS TOP PRUNING
50+You can further prune the tree by specifying a 'top' package. When a top
51+package is also specified, the rdepends tree is processed again to prune out
52+all dependency chains that do not include the top package. The result is a
53+tree that shows all dependency chains that include the top package and that
54+lead to the bottom package. For example, you can prune the ubuntu-desktop tree
55+with a top package of update-manager and a bottom package of aptdaemon. This
56+produces a tightly focused graph.
57+.SS TARGETED, FAST REVERSE DEPENDS GRAPHS
58+This approach allows you to create a final dot file that is small enough that
59+it can be converted into a png very quickly. (Typically reverse dependencies
60+trees are very large. It takes a very long time to generate a png for them and
61+the png is so crowded and the text is so small that they are very hard to
62+read.)
63+.SS DONT DRAW INTERMEDIATE GRAPHS
64+Since png creation (with the dot tool) is often the bottleneck that prevents
65+creating rdepends dot files and pngs, we can skip creating the png until the
66+final, pruned dot file is produced. This is done with dotdepends 'don't run dot'
67+mode, set with the -d argument. So, one could create the first
68+dot file for ubuntu-desktop without creating a png for it. Then use
69+dotdepends-prune to create the final dot file and convert that to a png.
70+.SH EXAMPLE
71+.PP
72+Create the initial dot file for ubuntu-desktop in depends mode, with recommends
73+included, and in 'don't draw mode':
74+.PP
75+dotdepends -m depends -r -d ubuntu-desktop
76+.PP
77+That command produces this initial dot file that defines the scope from which
78+further pruning can be done:
79+ubuntu-desktop/ubuntu-desktop-depends-Recommends-NoSuggests-NoEnhances.dot
80+.PP
81+Top prune that dot file to update-manager, bottom prune it to
82+aptdaemon, and pipe it to dot to create a png whose default node shape is 'box':
83+.PP
84+dotdepends-prune -t update-manager -b aptdaemon
85+ubuntu-desktop/ubuntu-desktop-depends-Recommends-NoSuggests-NoEnhances.dot |
86+dot -Tpng -Nshape=box > my.png
87+.br
88+.SH OPTIONS
89+\fB\-t\fR TOPPACKAGE, \fB\-\-top\-package\fR=\fITOPPACKAGE\fR
90+.PP
91+Dot tree is pruned to only include dependency chains
92+between an optional 'top' package node and a required 'bottom' package node.
93+This argument specifies the 'top' package node. When it is omitted, the
94+generated tree includes all dependency chains leading to the bottom package.
95+.HP
96+\fB\-b\fR BOTTOMPACKAGE, \fB\-\-bottom\-package\fR=\fIBOTTOMPACKAGE\fR
97+.PP
98+This argument specifies the required 'bottom' package node to prune the tree
99+to. This generates a tree of all reverse dependencies to the bottom package
100+found in the dot file being processed.
101+.PP
102+.SH COLOR CODING
103+.PP
104+\fBBlack arrows\fR show depends relationships.
105+.HP
106+\fBRed arrows\fR show recommends relationships.
107+.br
108+.SH AUTHOR
109+This manpage was written by Kyle Nitzsche <kyle.nitzsche@canonical.com>
110
111=== modified file 'debian/changelog'
112--- debian/changelog 2012-07-16 17:05:10 +0000
113+++ debian/changelog 2012-09-07 18:40:25 +0000
114@@ -1,3 +1,11 @@
115+dotdepends (0.3.19dev1) UNRELEASED; urgency=low
116+
117+ * add dotdepends-prune: a new script that top and bottom prunes dot file
118+ trees to produce smaller, targeted rdepends graphs for efficient conversion
119+ to pngs and its man page.
120+
121+ -- Kyle Nitzsche <kyle.nitzsche@canonical.com> Fri, 07 Sep 2012 12:57:56 -0400
122+
123 dotdepends (0.3.19) precise; urgency=low
124
125 * test/test_Dot.py: Add a unittest suite for the Dot object and populate with
126
127=== modified file 'debian/manpages'
128--- debian/manpages 2012-07-09 14:42:21 +0000
129+++ debian/manpages 2012-09-07 18:40:25 +0000
130@@ -1,2 +1,3 @@
131 data/dotdepends.1
132 data/dotdepends-merge.1
133+data/dotdepends-prune.1
134
135=== modified file 'usr/bin/dotdepends'
136--- usr/bin/dotdepends 2012-07-16 15:23:44 +0000
137+++ usr/bin/dotdepends 2012-09-07 18:40:25 +0000
138@@ -427,7 +427,7 @@
139 node = ""
140 if v == pkg:
141 styles['label'] = v
142- styles['fillecolor'] = "blue"
143+ styles['fillcolor'] = "blue"
144 styles['style'] = "filled"
145 styles['fontcolor'] = "white"
146 else:
147
148=== added file 'usr/bin/dotdepends-prune'
149--- usr/bin/dotdepends-prune 1970-01-01 00:00:00 +0000
150+++ usr/bin/dotdepends-prune 2012-09-07 18:40:25 +0000
151@@ -0,0 +1,182 @@
152+#!/usr/bin/python
153+#
154+# Copyright 2012 Canonical Ltd
155+# Authors:
156+# Kyle Nitzsche <kyle.nitzsche@canonical.com>
157+#
158+# This program is free software: you can redistribute it and/or modify
159+# it under the terms of the GNU General Public License as published by
160+# the Free Software Foundation, version 3 of the License.
161+#
162+# This program is distributed in the hope that it will be useful,
163+# but WITHOUT ANY WARRANTY; without even the implied warranty of
164+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
165+# GNU General Public License for more details.
166+#
167+# You should have received a copy of the GNU General Public License
168+# along with this program. If not, see <http://www.gnu.org/licenses/>.
169+from __future__ import print_function
170+from optparse import OptionParser
171+import Dotdepends.Dot
172+import sys
173+import os
174+
175+def pruneChainsToBottomPkg(btmPkg, chain):
176+ """Searches through the dot file and finds every chain of dependencies that
177+ leads to the bottomPkg node (reversely depends on it). Each such chain
178+ is appended to global 'chains' var."""
179+
180+ lnodes = dot.get_lnodes_for_rnode(btmPkg)
181+ for lnode in lnodes:
182+ if lnode in processedLnodes: continue
183+ else: processedLnodes.append(lnode)
184+ newChain = list(chain)
185+ newChain.append(lnode)
186+ if chain in chains: chains.remove(chain)
187+ chains.append(newChain)
188+ pruneChainsToBottomPkg(lnode,newChain)
189+
190+if __name__ == "__main__":
191+
192+ # dotdepends with no arguments or with -h should run help
193+ if (
194+ len(sys.argv) == 1
195+ or sys.argv[1] == '-h'
196+ or sys.argv[1].find('help')
197+ ) > -1:
198+ print("See 'man dotdepends-prune' for help", file=sys.stderr)
199+ sys.exit(0)
200+
201+ # command help is in the dotdepends man page
202+
203+ parser = OptionParser()
204+
205+ parser.add_option(
206+ "-t", "--top-package",
207+ dest="topPkg",
208+ type="string",
209+ default="",
210+ )
211+ parser.add_option(
212+ "-b", "--bottom-package",
213+ dest="bottomPkg",
214+ type="string",
215+ default="",
216+ )
217+ (opt, args) = parser.parse_args()
218+
219+ dotfile = args[0]
220+
221+ if len(opt.bottomPkg) < 2:
222+ print(
223+ "Error: You must specify the 'bottom' package to prune the tree to.",
224+ "Stopping."
225+ )
226+ sys.exit(1)
227+ if len(dotfile) < 1:
228+ print(
229+ "Error: You must specify the dot file to process.",
230+ "Stopping."
231+ )
232+ sys.exit(1)
233+
234+ if not os.path.exists(dotfile):
235+ print(
236+ "Error: ",
237+ dotfile,
238+ "does not exist."
239+ "Stopping."
240+ )
241+ sys.exit(1)
242+
243+ dot = Dotdepends.Dot.Dot(dotfile)
244+ bottomPkg = dot.pkgNode[opt.bottomPkg]
245+ hasTop = False
246+ if len(opt.topPkg) > 0:
247+ hasTop = True
248+ top = dot.pkgNode[opt.topPkg]
249+ joins = dict()
250+ chains = [[bottomPkg]]
251+ processedLnodes = []
252+
253+ pruneChainsToBottomPkg(bottomPkg, chains[0])
254+
255+ # now ,'chains' has every linear dependency chain that leads to
256+ # our bottomPkg. That is, we have 'pruned' the full tree to include
257+ # only chains rooted in our bottomPkg.
258+
259+ prunedChains = []
260+
261+ # Next, if a topPkg is specified, we need to prune the tree to exclude
262+ # all pkgs that do not include our top pkg.
263+
264+ if hasTop:
265+ for chain in chains:
266+ if top not in chain: continue
267+ prunedChains.append(chain)
268+ topPkg = opt.topPkg
269+ else:
270+ prunedChains = chains
271+ topPkg = "None"
272+ # time to print a dot file that consists of a header, content, and footer
273+
274+ for line in dot.header:
275+ if not line.startswith("label"): # print extant header but not label
276+ print(line.rstrip('\n'), sep="")
277+ # make label for current prune and print
278+ print(
279+ 'label="',
280+ "Dotdepends graph pruned to:\\nTop package: ",
281+ topPkg,
282+ ". Bottom package: ",
283+ dot.nodePkg[bottomPkg],
284+ '"',
285+ sep=""
286+ )
287+
288+ # print the pruned tree chains. each chain consists of a series of joins,
289+ # with one join per line. each join is two nodes followed by a style.
290+ # the style (drawn from the dot file for the join), sets the color of the
291+ # arrow in the graph: red for recommends, unset (black) for depends.
292+ for chain in prunedChains:
293+ idx = -1
294+ for item in chain:
295+ idx += 1
296+ if idx == 0: continue
297+ found = False
298+ for line in dot.joins:
299+ if line[0] == chain[idx] and line[1] == chain[idx-1]:
300+ found = True
301+ print(chain[idx], " -> ", chain[idx-1], line[2], ';', sep="")
302+ if not found:
303+ print(
304+ "Error: join not found in dot: ",
305+ chain[idx],
306+ " -> ",
307+ chain[idx-1],
308+ " Stopping."
309+ )
310+ sys.exit(1)
311+
312+ # print nodes. each node is a dot-style node name followed
313+ # by a style that contains a label that shows the pkg nam
314+ printedNodes = []
315+ for chain in prunedChains:
316+ for node in chain:
317+ if node in printedNodes: continue
318+ printedNodes.append(node)
319+ if node in dot.nodes.keys():
320+ print(
321+ node,
322+ ' [',
323+ dot.nodes[node],
324+ '];',
325+ sep=""
326+ )
327+
328+ # print footer
329+ print(dot.footer, sep="")
330+
331+ sys.exit()
332+
333+

Subscribers

People subscribed via source and target branches

to all changes: