Merge lp:~stgraber/upstart/upstart-initctl2dot-python3 into lp:upstart
- upstart-initctl2dot-python3
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 1392 | ||||
Proposed branch: | lp:~stgraber/upstart/upstart-initctl2dot-python3 | ||||
Merge into: | lp:upstart | ||||
Diff against target: |
917 lines (+386/-436) 1 file modified
scripts/initctl2dot.py (+386/-436) |
||||
To merge this branch: | bzr merge lp:~stgraber/upstart/upstart-initctl2dot-python3 | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
James Hunt | Approve | ||
Barry Warsaw (community) | Approve | ||
Review via email: mp+136721@code.launchpad.net |
Commit message
Description of the change
This branch updates initctl2dot to work with both python2 and python3.
The tested versions were python2.7 and python3.3.
The main changes are:
- Fixing a bunch of errors spotted by pyflakes.
- Added universal_
- Stop using string.split, instead split use <str>.split
- Re-indent the code, split lines, ... to please pep8
I also fixed a small bug where if a job name contained a dot, the .dot file
would be invalid. I'm simply replacing dots by underscores which appears to do
the trick.
The code should now be PEP-8 clean and the output appears to work as expected
when fed to graphviz.
Steve Langasek (vorlon) wrote : | # |
- 1396. By Stéphane Graber
-
Switch interpreter to python3 (code still works under python2)
Stéphane Graber (stgraber) wrote : | # |
> On Wed, Nov 28, 2012 at 05:10:25PM -0000, Stéphane Graber wrote:
> > === modified file 'scripts/
> > --- scripts/
> > +++ scripts/
> > @@ -50,522 +50,530 @@
> > import re
> > import fnmatch
> > import os
> > -from string import split
> > import datetime
> > from subprocess import (Popen, PIPE)
> > from optparse import OptionParser
>
> Shouldn't this also change the interpreter to /usr/bin/python3? I don't see
> any reason why we would need this script to be bilingual, we ought to just
> change it and go. If people want to use newer versions of upstart on OSes
> that don't have python3, then they just won't be able to use this script...
> but this is hardly a requirement anyway.
Oops, even though I named the branch -python3 I completely forgot to change the default interpreter to python3 :)
Done now.
> (Also, looking at this I notice we're shipping initctl2dot in /bin in the
> Ubuntu package... that doesn't make much sense when the interpreter is in
> /usr/bin.)
Good point, we should change the packaging to put the script in /usr/bin.
Barry Warsaw (barry) wrote : | # |
Just a couple more suggestions, since why not.
* No parens are needed in `from subprocess import Popen, PIPE`
* How about switching to argparse instead of optparse?
* In header(), the global statement isn't necessary because you're not assigning to options
* I'd probably rewrite the concats in header() into a triple-quoted multiline string, with {options.foo} replacements
* Remove the global statement from footer()
* There should be some better way to get rid of the multiple concats in footer() too
* sanitize() seems pretty inefficient. maybe that doesn't matter for this script, but it might be better written with a re.sub() where `repl` is a function that knows the mappings
* Why does mk_node_name() even exist? ;)
* show_events(): remove global and rewrite the concats
* Remove the globals from show_events()
* Remove the global in show_jobs()
* Remove the globals in show_jobs()
* In show_jobs(), the `if not restrictions_list: return` is kind of unnecessary.
* None of the show_*() methods need their globals either (just keep at it for the rest of this file ;) - you only need globals if you're *assigning* to a global variable (not if just referencing it, or calling methods like .append() on it)
* read_data(): `for line in ifh:` is probably good enough
* Might want to use a context manager to handle ifh in read_data(). For Python 3.3, see http://
* line 430: extra parens
Probably other stuff to be cleaned up, but those are the major things that jump out at me. I can take a crack at it if you want.
- 1397. By Stéphane Graber
-
Cleanup subprocess import and use argparse instead of optparse
- 1398. By Stéphane Graber
-
Drop all unneeded global statements and do some extra code simplification.
- 1399. By Stéphane Graber
-
Remove some more globals and unused variable
- 1400. By Stéphane Graber
-
Simplify header and footer functions
- 1401. By Stéphane Graber
-
Fix indent
- 1402. By Stéphane Graber
-
Move the code replacing dots by underscores to the sanitise function
Stéphane Graber (stgraber) wrote : | # |
> Just a couple more suggestions, since why not.
>
> * No parens are needed in `from subprocess import Popen, PIPE`
Done
> * How about switching to argparse instead of optparse?
Done
> * In header(), the global statement isn't necessary because you're not
> assigning to options
Done
> * I'd probably rewrite the concats in header() into a triple-quoted multiline
> string, with {options.foo} replacements
Done
> * Remove the global statement from footer()
Done
> * There should be some better way to get rid of the multiple concats in
> footer() too
Done. Pre-processed the ifs, then used a giant string with format, similar to header()
> * sanitize() seems pretty inefficient. maybe that doesn't matter for this
> script, but it might be better written with a re.sub() where `repl` is a
> function that knows the mappings
Kept it as-is for now as I'm not a fan of using regexps when not absolutely necessary :)
> * Why does mk_node_name() even exist? ;)
No good reason apparently, dropped.
> * show_events(): remove global and rewrite the concats
Done.
> * Remove the globals from show_events()
Done
> * Remove the global in show_jobs()
Done
> * Remove the globals in show_jobs()
Done
> * In show_jobs(), the `if not restrictions_list: return` is kind of
> unnecessary.
Right, at first read, I assumed that restrictions_list could be None, but rechecking the code, that's not actually the case, so iterating will always work and that if statement is unnecessary. Dropped.
> * None of the show_*() methods need their globals either (just keep at it for
> the rest of this file ;) - you only need globals if you're *assigning* to a
> global variable (not if just referencing it, or calling methods like .append()
> on it)
Done
> * read_data(): `for line in ifh:` is probably good enough
Agreed, done.
> * Might want to use a context manager to handle ifh in read_data(). For Python
> 3.3, see http://
I'd rather not depend on python 3.3. Some people may still want to use the fixed script on something slightly older, but that's good to know nevertheless.
> * line 430: extra parens
Fixed that one and another one a few lines further down.
> Probably other stuff to be cleaned up, but those are the major things that
> jump out at me. I can take a crack at it if you want.
As far as I can tell, the script still works with those changes and the output is similar.
Barry Warsaw (barry) wrote : | # |
Much improved, thanks! Just a few more to go:
* Remove global jobs, events from read_data()
* Remove global jobs, cmd, default_color_* from main(), but keep global
options and restrictions_list.
I'll go ahead and approve the merge on the above conditions.
Barry Warsaw (barry) : | # |
- 1403. By Stéphane Graber
-
Remove last useless globals, fix .dot file syntax in footer, fix indentation of output file.
Stéphane Graber (stgraber) wrote : | # |
Thanks for the comments. I removed those last few globals and fixed a mistake I did in the output file so that it's now valid. While I was at it, I also tweaked the code a bit so that the output file's indentation is correct.
James Hunt (jamesodhunt) wrote : | # |
Thanks Stéphane. I found a new bug in testing (bug 1084985), but that is unrelated to the changes you've made, so merged.
Preview Diff
1 | === modified file 'scripts/initctl2dot.py' |
2 | --- scripts/initctl2dot.py 2011-06-06 12:52:08 +0000 |
3 | +++ scripts/initctl2dot.py 2012-11-29 16:35:44 +0000 |
4 | @@ -1,4 +1,4 @@ |
5 | -#!/usr/bin/python |
6 | +#!/usr/bin/python3 |
7 | # -*- coding: utf-8 -*- |
8 | #--------------------------------------------------------------------- |
9 | # |
10 | @@ -50,522 +50,472 @@ |
11 | import re |
12 | import fnmatch |
13 | import os |
14 | -from string import split |
15 | import datetime |
16 | -from subprocess import (Popen, PIPE) |
17 | -from optparse import OptionParser |
18 | +from subprocess import Popen, PIPE |
19 | +from argparse import ArgumentParser |
20 | |
21 | -jobs = {} |
22 | +options = None |
23 | +jobs = {} |
24 | events = {} |
25 | cmd = "initctl --system show-config -e" |
26 | -script_name = os.path.basename(sys.argv[0]) |
27 | - |
28 | -job_events = [ 'starting', 'started', 'stopping', 'stopped' ] |
29 | +script_name = os.path.basename(sys.argv[0]) |
30 | |
31 | # list of jobs to restict output to |
32 | restrictions_list = [] |
33 | |
34 | -default_color_emits = 'green' |
35 | +default_color_emits = 'green' |
36 | default_color_start_on = 'blue' |
37 | -default_color_stop_on = 'red' |
38 | -default_color_event = 'thistle' |
39 | -default_color_job = '#DCDCDC' # "Gainsboro" |
40 | -default_color_text = 'black' |
41 | -default_color_bg = 'white' |
42 | +default_color_stop_on = 'red' |
43 | +default_color_event = 'thistle' |
44 | +default_color_job = '#DCDCDC' # "Gainsboro" |
45 | +default_color_text = 'black' |
46 | +default_color_bg = 'white' |
47 | |
48 | -default_outfile = 'upstart.dot' |
49 | +default_outfile = 'upstart.dot' |
50 | |
51 | |
52 | def header(ofh): |
53 | - global options |
54 | - |
55 | - str = "digraph upstart {\n" |
56 | - |
57 | - # make the default node an event to simplify glob code |
58 | - str += " node [shape=\"diamond\", fontcolor=\"%s\", fillcolor=\"%s\", style=\"filled\"];\n" \ |
59 | - % (options.color_event_text, options.color_event) |
60 | - str += " rankdir=LR;\n" |
61 | - str += " overlap=false;\n" |
62 | - str += " bgcolor=\"%s\";\n" % options.color_bg |
63 | - str += " fontcolor=\"%s\";\n" % options.color_text |
64 | - |
65 | - ofh.write(str) |
66 | + ofh.write("""digraph upstart {{ |
67 | + node [shape=\"diamond\", fontcolor=\"{options.color_event_text}\", """ |
68 | + """fillcolor=\"{options.color_event}\", style=\"filled\"]; |
69 | + rankdir=LR; |
70 | + overlap=false; |
71 | + bgcolor=\"{options.color_bg}\"; |
72 | + fontcolor=\"{options.color_text}\"; |
73 | +""".format(options=options)) |
74 | |
75 | |
76 | def footer(ofh): |
77 | - global options |
78 | - |
79 | - epilog = "overlap=false;\n" |
80 | - epilog += "label=\"Generated on %s by %s\\n" % \ |
81 | - (str(datetime.datetime.now()), script_name) |
82 | - |
83 | - if options.restrictions: |
84 | - epilog += "(subset, " |
85 | - else: |
86 | - epilog += "(" |
87 | - |
88 | - if options.infile: |
89 | - epilog += "from file data).\\n" |
90 | - else: |
91 | - epilog += "from '%s' on host %s).\\n" % \ |
92 | - (cmd, os.uname()[1]) |
93 | - |
94 | - epilog += "Boxes of color %s denote jobs.\\n" % options.color_job |
95 | - epilog += "Solid diamonds of color %s denote events.\\n" % options.color_event |
96 | - epilog += "Dotted diamonds denote 'glob' events.\\n" |
97 | - epilog += "Emits denoted by %s lines.\\n" % options.color_emits |
98 | - epilog += "Start on denoted by %s lines.\\n" % options.color_start_on |
99 | - epilog += "Stop on denoted by %s lines.\\n" % options.color_stop_on |
100 | - epilog += "\";\n" |
101 | - epilog += "}\n" |
102 | - ofh.write(epilog) |
103 | + if options.restrictions: |
104 | + details = "(subset, " |
105 | + else: |
106 | + details = "(" |
107 | + |
108 | + if options.infile: |
109 | + details += "from file data)." |
110 | + else: |
111 | + details += "from '%s' on host %s)." % (cmd, os.uname()[1]) |
112 | + |
113 | + ofh.write(" overlap=false;\n" |
114 | + " label=\"Generated on {datenow} by {script_name} {details}\\n" |
115 | + "Boxes of color {options.color_job} denote jobs.\\n" |
116 | + "Solid diamonds of color {options.color_event} denote events.\\n" |
117 | + "Dotted diamons denote 'glob' events.\\n" |
118 | + "Emits denoted by {options.color_emits} lines.\\n" |
119 | + "Start on denoted by {options.color_start_on} lines.\\n" |
120 | + "Stop on denoted by {options.color_stop_on} lines.\\n" |
121 | + "\";\n" |
122 | + "}}\n".format(options=options, datenow=datetime.datetime.now(), |
123 | + script_name=script_name, details=details)) |
124 | |
125 | |
126 | # Map dash to underscore since graphviz node names cannot |
127 | # contain dashes. Also remove dollars and colons |
128 | def sanitise(s): |
129 | - return s.replace('-', '_').replace('$', 'dollar_').replace('[', \ |
130 | - 'lbracket').replace(']', 'rbracket').replace('!', \ |
131 | - 'bang').replace(':', '_').replace('*', 'star').replace('?', 'question') |
132 | + return s.replace('-', '_').replace('$', 'dollar_') \ |
133 | + .replace('[', 'lbracket').replace(']', 'rbracket') \ |
134 | + .replace('!', 'bang').replace(':', 'colon').replace('*', 'star') \ |
135 | + .replace('?', 'question').replace('.', '_') |
136 | |
137 | |
138 | # Convert a dollar in @name to a unique-ish new name, based on @job and |
139 | # return it. Used for very rudimentary instance handling. |
140 | def encode_dollar(job, name): |
141 | - if name[0] == '$': |
142 | - name = job + ':' + name |
143 | - return name |
144 | - |
145 | - |
146 | -def mk_node_name(name): |
147 | - return sanitise(name) |
148 | + if name[0] == '$': |
149 | + name = job + ':' + name |
150 | + return name |
151 | |
152 | |
153 | # Jobs and events can have identical names, so prefix them to namespace |
154 | # them off. |
155 | def mk_job_node_name(name): |
156 | - return mk_node_name('job_' + name) |
157 | + return sanitise('job_' + name) |
158 | |
159 | |
160 | def mk_event_node_name(name): |
161 | - return mk_node_name('event_' + name) |
162 | + return sanitise('event_' + name) |
163 | |
164 | |
165 | def show_event(ofh, name): |
166 | - global options |
167 | - str = "%s [label=\"%s\", shape=diamond, fontcolor=\"%s\", fillcolor=\"%s\"," % \ |
168 | - (mk_event_node_name(name), name, options.color_event_text, options.color_event) |
169 | + str = " %s [label=\"%s\", shape=diamond, fontcolor=\"%s\", " \ |
170 | + "fillcolor=\"%s\"," % (mk_event_node_name(name), name, |
171 | + options.color_event_text, options.color_event) |
172 | |
173 | if '*' in name: |
174 | - str += " style=\"dotted\"" |
175 | + str += " style=\"dotted\"" |
176 | else: |
177 | - str += " style=\"filled\"" |
178 | + str += " style=\"filled\"" |
179 | |
180 | str += "];\n" |
181 | |
182 | ofh.write(str) |
183 | |
184 | + |
185 | def show_events(ofh): |
186 | - global events |
187 | - global options |
188 | - global restrictions_list |
189 | - |
190 | - events_to_show = [] |
191 | - |
192 | - if restrictions_list: |
193 | - for job in restrictions_list: |
194 | - |
195 | - # We want all events emitted by the jobs in the restrictions_list. |
196 | - events_to_show += jobs[job]['emits'] |
197 | - |
198 | - # We also want all events that jobs in restrictions_list start/stop |
199 | - # on. |
200 | - events_to_show += jobs[job]['start on']['event'] |
201 | - events_to_show += jobs[job]['stop on']['event'] |
202 | - |
203 | - # We also want all events emitted by all jobs that jobs in the |
204 | - # restrictions_list start/stop on. Finally, we want all events |
205 | - # emmitted by those jobs in the restrictions_list that we |
206 | - # start/stop on. |
207 | - for j in jobs[job]['start on']['job']: |
208 | - if jobs.has_key(j) and jobs[j].has_key('emits'): |
209 | - events_to_show += jobs[j]['emits'] |
210 | - |
211 | - for j in jobs[job]['stop on']['job']: |
212 | - if jobs.has_key(j) and jobs[j].has_key('emits'): |
213 | - events_to_show += jobs[j]['emits'] |
214 | - else: |
215 | - events_to_show = events |
216 | - |
217 | - for e in events_to_show: |
218 | - show_event(ofh, e) |
219 | + events_to_show = [] |
220 | + |
221 | + if restrictions_list: |
222 | + for job in restrictions_list: |
223 | + |
224 | + # We want all events emitted by the jobs in the restrictions_list. |
225 | + events_to_show += jobs[job]['emits'] |
226 | + |
227 | + # We also want all events that jobs in restrictions_list start/stop |
228 | + # on. |
229 | + events_to_show += jobs[job]['start on']['event'] |
230 | + events_to_show += jobs[job]['stop on']['event'] |
231 | + |
232 | + # We also want all events emitted by all jobs that jobs in the |
233 | + # restrictions_list start/stop on. Finally, we want all events |
234 | + # emmitted by those jobs in the restrictions_list that we |
235 | + # start/stop on. |
236 | + for j in jobs[job]['start on']['job']: |
237 | + if j in jobs and 'emits' in jobs[j]: |
238 | + events_to_show += jobs[j]['emits'] |
239 | + |
240 | + for j in jobs[job]['stop on']['job']: |
241 | + if j in jobs and 'emits' in jobs[j]: |
242 | + events_to_show += jobs[j]['emits'] |
243 | + else: |
244 | + events_to_show = events |
245 | + |
246 | + for e in events_to_show: |
247 | + show_event(ofh, e) |
248 | |
249 | |
250 | def show_job(ofh, name): |
251 | - global options |
252 | - |
253 | - ofh.write(""" |
254 | - %s [shape=\"record\", label=\"<job> %s | { <start> start on | <stop> stop on }\", fontcolor=\"%s\", style=\"filled\", fillcolor=\"%s\"]; |
255 | - """ % (mk_job_node_name(name), name, options.color_job_text, options.color_job)) |
256 | + ofh.write(" %s [shape=\"record\", label=\"<job> %s | { <start> start on |" |
257 | + " <stop> stop on }\", fontcolor=\"%s\", style=\"filled\", " |
258 | + " fillcolor=\"%s\"];\n" % (mk_job_node_name(name), name, |
259 | + options.color_job_text, |
260 | + options.color_job)) |
261 | |
262 | |
263 | def show_jobs(ofh): |
264 | - global jobs |
265 | - global options |
266 | - global restrictions_list |
267 | - |
268 | - if restrictions_list: |
269 | - jobs_to_show = restrictions_list |
270 | - else: |
271 | - jobs_to_show = jobs |
272 | - |
273 | - for j in jobs_to_show: |
274 | - show_job(ofh, j) |
275 | - # add those jobs which are referenced by existing jobs, but which |
276 | - # might not be available as .conf files. For example, plymouth.conf |
277 | - # references gdm *or* kdm, but you are unlikely to have both |
278 | - # installed. |
279 | - for s in jobs[j]['start on']['job']: |
280 | - if s not in jobs_to_show: |
281 | - show_job(ofh, s) |
282 | - |
283 | - for s in jobs[j]['stop on']['job']: |
284 | - if s not in jobs_to_show: |
285 | - show_job(ofh, s) |
286 | - |
287 | - if not restrictions_list: |
288 | - return |
289 | - |
290 | - # Having displayed the jobs in restrictions_list, |
291 | - # we now need to display all jobs that *those* jobs |
292 | - # start on/stop on. |
293 | - for j in restrictions_list: |
294 | - for job in jobs[j]['start on']['job']: |
295 | - show_job(ofh, job) |
296 | - for job in jobs[j]['stop on']['job']: |
297 | - show_job(ofh, job) |
298 | - |
299 | - # Finally, show all jobs which emit events that jobs in the |
300 | - # restrictions_list care about. |
301 | - for j in restrictions_list: |
302 | - |
303 | - for e in jobs[j]['start on']['event']: |
304 | - for k in jobs: |
305 | - if e in jobs[k]['emits']: |
306 | - show_job(ofh, k) |
307 | - |
308 | - for e in jobs[j]['stop on']['event']: |
309 | - for k in jobs: |
310 | - if e in jobs[k]['emits']: |
311 | - show_job(ofh, k) |
312 | + if restrictions_list: |
313 | + jobs_to_show = restrictions_list |
314 | + else: |
315 | + jobs_to_show = jobs |
316 | + |
317 | + for j in jobs_to_show: |
318 | + show_job(ofh, j) |
319 | + # add those jobs which are referenced by existing jobs, but which |
320 | + # might not be available as .conf files. For example, plymouth.conf |
321 | + # references gdm *or* kdm, but you are unlikely to have both |
322 | + # installed. |
323 | + for s in jobs[j]['start on']['job']: |
324 | + if s not in jobs_to_show: |
325 | + show_job(ofh, s) |
326 | + |
327 | + for s in jobs[j]['stop on']['job']: |
328 | + if s not in jobs_to_show: |
329 | + show_job(ofh, s) |
330 | + |
331 | + # Having displayed the jobs in restrictions_list, |
332 | + # we now need to display all jobs that *those* jobs |
333 | + # start on/stop on. |
334 | + for j in restrictions_list: |
335 | + for job in jobs[j]['start on']['job']: |
336 | + show_job(ofh, job) |
337 | + for job in jobs[j]['stop on']['job']: |
338 | + show_job(ofh, job) |
339 | + |
340 | + # Finally, show all jobs which emit events that jobs in the |
341 | + # restrictions_list care about. |
342 | + for j in restrictions_list: |
343 | + for e in jobs[j]['start on']['event']: |
344 | + for k in jobs: |
345 | + if e in jobs[k]['emits']: |
346 | + show_job(ofh, k) |
347 | + |
348 | + for e in jobs[j]['stop on']['event']: |
349 | + for k in jobs: |
350 | + if e in jobs[k]['emits']: |
351 | + show_job(ofh, k) |
352 | |
353 | |
354 | def show_edge(ofh, from_node, to_node, color): |
355 | - ofh.write("%s -> %s [color=\"%s\"];\n" % (from_node, to_node, color)) |
356 | + ofh.write(" %s -> %s [color=\"%s\"];\n" % (from_node, to_node, color)) |
357 | |
358 | |
359 | def show_start_on_job_edge(ofh, from_job, to_job): |
360 | - global options |
361 | - show_edge(ofh, "%s:start" % mk_job_node_name(from_job), |
362 | - "%s:job" % mk_job_node_name(to_job), options.color_start_on) |
363 | + show_edge(ofh, "%s:start" % mk_job_node_name(from_job), |
364 | + "%s:job" % mk_job_node_name(to_job), options.color_start_on) |
365 | |
366 | |
367 | def show_start_on_event_edge(ofh, from_job, to_event): |
368 | - global options |
369 | - show_edge(ofh, "%s:start" % mk_job_node_name(from_job), |
370 | - mk_event_node_name(to_event), options.color_start_on) |
371 | + show_edge(ofh, "%s:start" % mk_job_node_name(from_job), |
372 | + mk_event_node_name(to_event), options.color_start_on) |
373 | |
374 | |
375 | def show_stop_on_job_edge(ofh, from_job, to_job): |
376 | - global options |
377 | - show_edge(ofh, "%s:stop" % mk_job_node_name(from_job), |
378 | - "%s:job" % mk_job_node_name(to_job), options.color_stop_on) |
379 | + show_edge(ofh, "%s:stop" % mk_job_node_name(from_job), |
380 | + "%s:job" % mk_job_node_name(to_job), options.color_stop_on) |
381 | |
382 | |
383 | def show_stop_on_event_edge(ofh, from_job, to_event): |
384 | - global options |
385 | - show_edge(ofh, "%s:stop" % mk_job_node_name(from_job), |
386 | - mk_event_node_name(to_event), options.color_stop_on) |
387 | + show_edge(ofh, "%s:stop" % mk_job_node_name(from_job), |
388 | + mk_event_node_name(to_event), options.color_stop_on) |
389 | |
390 | |
391 | def show_job_emits_edge(ofh, from_job, to_event): |
392 | - global options |
393 | - show_edge(ofh, "%s:job" % mk_job_node_name(from_job), |
394 | - mk_event_node_name(to_event), options.color_emits) |
395 | + show_edge(ofh, "%s:job" % mk_job_node_name(from_job), |
396 | + mk_event_node_name(to_event), options.color_emits) |
397 | |
398 | |
399 | def show_edges(ofh): |
400 | - global events |
401 | - global jobs |
402 | - global options |
403 | - global restrictions_list |
404 | - |
405 | - glob_jobs = {} |
406 | - |
407 | - if restrictions_list: |
408 | - jobs_list = restrictions_list |
409 | - else: |
410 | - jobs_list = jobs |
411 | - |
412 | - for job in jobs_list: |
413 | - |
414 | - for s in jobs[job]['start on']['job']: |
415 | - show_start_on_job_edge(ofh, job, s) |
416 | - |
417 | - for s in jobs[job]['start on']['event']: |
418 | - show_start_on_event_edge(ofh, job, s) |
419 | - |
420 | - for s in jobs[job]['stop on']['job']: |
421 | - show_stop_on_job_edge(ofh, job, s) |
422 | - |
423 | - for s in jobs[job]['stop on']['event']: |
424 | - show_stop_on_event_edge(ofh, job, s) |
425 | - |
426 | - for e in jobs[job]['emits']: |
427 | - if '*' in e: |
428 | - # handle glob patterns in 'emits' |
429 | - glob_events = [] |
430 | - for _e in events: |
431 | - if e != _e and fnmatch.fnmatch(_e, e): |
432 | - glob_events.append(_e) |
433 | - glob_jobs[job] = glob_events |
434 | - |
435 | - show_job_emits_edge(ofh, job, e) |
436 | + glob_jobs = {} |
437 | + |
438 | + if restrictions_list: |
439 | + jobs_list = restrictions_list |
440 | + else: |
441 | + jobs_list = jobs |
442 | + |
443 | + for job in jobs_list: |
444 | + for s in jobs[job]['start on']['job']: |
445 | + show_start_on_job_edge(ofh, job, s) |
446 | + |
447 | + for s in jobs[job]['start on']['event']: |
448 | + show_start_on_event_edge(ofh, job, s) |
449 | + |
450 | + for s in jobs[job]['stop on']['job']: |
451 | + show_stop_on_job_edge(ofh, job, s) |
452 | + |
453 | + for s in jobs[job]['stop on']['event']: |
454 | + show_stop_on_event_edge(ofh, job, s) |
455 | + |
456 | + for e in jobs[job]['emits']: |
457 | + if '*' in e: |
458 | + # handle glob patterns in 'emits' |
459 | + glob_events = [] |
460 | + for _e in events: |
461 | + if e != _e and fnmatch.fnmatch(_e, e): |
462 | + glob_events.append(_e) |
463 | + glob_jobs[job] = glob_events |
464 | + |
465 | + show_job_emits_edge(ofh, job, e) |
466 | + |
467 | + if not restrictions_list: |
468 | + continue |
469 | + |
470 | + # Add links to events emitted by all jobs which current job |
471 | + # start/stops on |
472 | + for j in jobs[job]['start on']['job']: |
473 | + if j not in jobs: |
474 | + continue |
475 | + for e in jobs[j]['emits']: |
476 | + show_job_emits_edge(ofh, j, e) |
477 | + |
478 | + for j in jobs[job]['stop on']['job']: |
479 | + for e in jobs[j]['emits']: |
480 | + show_job_emits_edge(ofh, j, e) |
481 | + |
482 | + # Create links from jobs (which advertise they emits a class of |
483 | + # events, via the glob syntax) to all the events they create. |
484 | + for g in glob_jobs: |
485 | + for ge in glob_jobs[g]: |
486 | + show_job_emits_edge(ofh, g, ge) |
487 | |
488 | if not restrictions_list: |
489 | - continue |
490 | - |
491 | - # Add links to events emitted by all jobs which current job |
492 | - # start/stops on |
493 | - for j in jobs[job]['start on']['job']: |
494 | - if not jobs.has_key(j): |
495 | - continue |
496 | - for e in jobs[j]['emits']: |
497 | - show_job_emits_edge(ofh, j, e) |
498 | - |
499 | - for j in jobs[job]['stop on']['job']: |
500 | - for e in jobs[j]['emits']: |
501 | - show_job_emits_edge(ofh, j, e) |
502 | - |
503 | - # Create links from jobs (which advertise they emits a class of |
504 | - # events, via the glob syntax) to all the events they create. |
505 | - for g in glob_jobs: |
506 | - for ge in glob_jobs[g]: |
507 | - show_job_emits_edge(ofh, g, ge) |
508 | - |
509 | - if not restrictions_list: |
510 | - return |
511 | - |
512 | - # Add jobs->event links to jobs which emit events that current job |
513 | - # start/stops on. |
514 | - for j in restrictions_list: |
515 | - |
516 | - for e in jobs[j]['start on']['event']: |
517 | - for k in jobs: |
518 | - if e in jobs[k]['emits'] and e not in restrictions_list: |
519 | - show_job_emits_edge(ofh, k, e) |
520 | - |
521 | - for e in jobs[j]['stop on']['event']: |
522 | - for k in jobs: |
523 | - if e in jobs[k]['emits'] and e not in restrictions_list: |
524 | - show_job_emits_edge(ofh, k, e) |
525 | + return |
526 | + |
527 | + # Add jobs->event links to jobs which emit events that current job |
528 | + # start/stops on. |
529 | + for j in restrictions_list: |
530 | + for e in jobs[j]['start on']['event']: |
531 | + for k in jobs: |
532 | + if e in jobs[k]['emits'] and e not in restrictions_list: |
533 | + show_job_emits_edge(ofh, k, e) |
534 | + |
535 | + for e in jobs[j]['stop on']['event']: |
536 | + for k in jobs: |
537 | + if e in jobs[k]['emits'] and e not in restrictions_list: |
538 | + show_job_emits_edge(ofh, k, e) |
539 | |
540 | |
541 | def read_data(): |
542 | - global jobs |
543 | - global events |
544 | - global options |
545 | - global cmd |
546 | - global job_events |
547 | - |
548 | - if options.infile: |
549 | - try: |
550 | - ifh = open(options.infile, 'r') |
551 | - except: |
552 | - sys.exit("ERROR: cannot read file '%s'" % options.infile) |
553 | - else: |
554 | - try: |
555 | - ifh = Popen(split(cmd), stdout=PIPE).stdout |
556 | - except: |
557 | - sys.exit("ERROR: cannot run '%s'" % cmd) |
558 | - |
559 | - for line in ifh.readlines(): |
560 | - record = {} |
561 | - line = line.rstrip() |
562 | - |
563 | - result = re.match('^\s+start on ([^,]+) \(job:\s*([^,]*), env:', line) |
564 | - if result: |
565 | - _event = encode_dollar(job, result.group(1)) |
566 | - _job = result.group(2) |
567 | - if _job: |
568 | - jobs[job]['start on']['job'][_job] = 1 |
569 | - else: |
570 | - jobs[job]['start on']['event'][_event] = 1 |
571 | - events[_event] = 1 |
572 | - continue |
573 | - |
574 | - result = re.match('^\s+stop on ([^,]+) \(job:\s*([^,]*), env:', line) |
575 | - if result: |
576 | - _event = encode_dollar(job, result.group(1)) |
577 | - _job = result.group(2) |
578 | - if _job: |
579 | - jobs[job]['stop on']['job'][_job] = 1 |
580 | - else: |
581 | - jobs[job]['stop on']['event'][_event] = 1 |
582 | - events[_event] = 1 |
583 | - continue |
584 | - |
585 | - if re.match('^\s+emits', line): |
586 | - event = (line.lstrip().split())[1] |
587 | - event = encode_dollar(job, event) |
588 | - events[event] = 1 |
589 | - jobs[job]['emits'][event] = 1 |
590 | - else: |
591 | - tokens = (line.lstrip().split()) |
592 | - |
593 | - if len(tokens) != 1: |
594 | - sys.exit("ERROR: invalid line: %s" % line.lstrip()) |
595 | - |
596 | - job_record = {} |
597 | - |
598 | - start_on = {} |
599 | - start_on_jobs = {} |
600 | - start_on_events = {} |
601 | - |
602 | - stop_on = {} |
603 | - stop_on_jobs = {} |
604 | - stop_on_events = {} |
605 | - |
606 | - emits = {} |
607 | - |
608 | - start_on['job'] = start_on_jobs |
609 | - start_on['event'] = start_on_events |
610 | - |
611 | - stop_on['job'] = stop_on_jobs |
612 | - stop_on['event'] = stop_on_events |
613 | - |
614 | - job_record['start on'] = start_on |
615 | - job_record['stop on'] = stop_on |
616 | - job_record['emits'] = emits |
617 | - |
618 | - job = (tokens)[0] |
619 | - jobs[job] = job_record |
620 | + if options.infile: |
621 | + try: |
622 | + ifh = open(options.infile, 'r') |
623 | + except: |
624 | + sys.exit("ERROR: cannot read file '%s'" % options.infile) |
625 | + else: |
626 | + try: |
627 | + ifh = Popen(cmd.split(), stdout=PIPE, |
628 | + universal_newlines=True).stdout |
629 | + except: |
630 | + sys.exit("ERROR: cannot run '%s'" % cmd) |
631 | + |
632 | + job = None |
633 | + for line in ifh: |
634 | + line = line.rstrip() |
635 | + |
636 | + result = re.match('^\s+start on ([^,]+) \(job:\s*([^,]*), env:', line) |
637 | + if result: |
638 | + _event = encode_dollar(job, result.group(1)) |
639 | + _job = result.group(2) |
640 | + if _job: |
641 | + jobs[job]['start on']['job'][_job] = 1 |
642 | + else: |
643 | + jobs[job]['start on']['event'][_event] = 1 |
644 | + events[_event] = 1 |
645 | + continue |
646 | + |
647 | + result = re.match('^\s+stop on ([^,]+) \(job:\s*([^,]*), env:', line) |
648 | + if result: |
649 | + _event = encode_dollar(job, result.group(1)) |
650 | + _job = result.group(2) |
651 | + if _job: |
652 | + jobs[job]['stop on']['job'][_job] = 1 |
653 | + else: |
654 | + jobs[job]['stop on']['event'][_event] = 1 |
655 | + events[_event] = 1 |
656 | + continue |
657 | + |
658 | + if re.match('^\s+emits', line): |
659 | + event = line.lstrip().split()[1] |
660 | + event = encode_dollar(job, event) |
661 | + events[event] = 1 |
662 | + jobs[job]['emits'][event] = 1 |
663 | + else: |
664 | + tokens = line.lstrip().split() |
665 | + |
666 | + if len(tokens) != 1: |
667 | + sys.exit("ERROR: invalid line: %s" % line.lstrip()) |
668 | + |
669 | + job_record = {} |
670 | + |
671 | + start_on = {} |
672 | + start_on_jobs = {} |
673 | + start_on_events = {} |
674 | + |
675 | + stop_on = {} |
676 | + stop_on_jobs = {} |
677 | + stop_on_events = {} |
678 | + |
679 | + emits = {} |
680 | + |
681 | + start_on['job'] = start_on_jobs |
682 | + start_on['event'] = start_on_events |
683 | + |
684 | + stop_on['job'] = stop_on_jobs |
685 | + stop_on['event'] = stop_on_events |
686 | + |
687 | + job_record['start on'] = start_on |
688 | + job_record['stop on'] = stop_on |
689 | + job_record['emits'] = emits |
690 | + |
691 | + job = (tokens)[0] |
692 | + jobs[job] = job_record |
693 | |
694 | |
695 | def main(): |
696 | - global jobs |
697 | - global options |
698 | - global cmd |
699 | - global default_color_emits |
700 | - global default_color_start_on |
701 | - global default_color_stop_on |
702 | - global default_color_event |
703 | - global default_color_job |
704 | - global default_color_text |
705 | - global default_color_bg |
706 | - global restrictions_list |
707 | - |
708 | - description = "Convert initctl(8) output to GraphViz dot(1) format." |
709 | - epilog = \ |
710 | - "See http://www.graphviz.org/doc/info/colors.html for available colours." |
711 | - |
712 | - parser = OptionParser(description=description, epilog=epilog) |
713 | - |
714 | - parser.add_option("-r", "--restrict-to-jobs", |
715 | - dest="restrictions", |
716 | - help="Limit display of 'start on' and 'stop on' conditions to " + |
717 | - "specified jobs (comma-separated list).") |
718 | - |
719 | - parser.add_option("-f", "--infile", |
720 | - dest="infile", |
721 | - help="File to read '%s' output from. If not specified, " \ |
722 | - "initctl will be run automatically." % cmd) |
723 | - |
724 | - parser.add_option("-o", "--outfile", |
725 | - dest="outfile", |
726 | - help="File to write output to (default=%s)" % default_outfile) |
727 | - |
728 | - parser.add_option("--color-emits", |
729 | - dest="color_emits", |
730 | - help="Specify color for 'emits' lines (default=%s)." % |
731 | - default_color_emits) |
732 | - |
733 | - parser.add_option("--color-start-on", |
734 | - dest="color_start_on", |
735 | - help="Specify color for 'start on' lines (default=%s)." % |
736 | - default_color_start_on) |
737 | - |
738 | - parser.add_option("--color-stop-on", |
739 | - dest="color_stop_on", |
740 | - help="Specify color for 'stop on' lines (default=%s)." % |
741 | - default_color_stop_on) |
742 | - |
743 | - parser.add_option("--color-event", |
744 | - dest="color_event", |
745 | - help="Specify color for event boxes (default=%s)." % |
746 | - default_color_event) |
747 | - |
748 | - parser.add_option("--color-text", |
749 | - dest="color_text", |
750 | - help="Specify color for summary text (default=%s)." % |
751 | - default_color_text) |
752 | - |
753 | - parser.add_option("--color-bg", |
754 | - dest="color_bg", |
755 | - help="Specify background color for diagram (default=%s)." % |
756 | - default_color_bg) |
757 | - |
758 | - parser.add_option("--color-event-text", |
759 | - dest="color_event_text", |
760 | - help="Specify color for text in event boxes (default=%s)." % |
761 | - default_color_text) |
762 | - |
763 | - parser.add_option("--color-job-text", |
764 | - dest="color_job_text", |
765 | - help="Specify color for text in job boxes (default=%s)." % |
766 | - default_color_text) |
767 | - |
768 | - parser.add_option("--color-job", |
769 | - dest="color_job", |
770 | - help="Specify color for job boxes (default=%s)." % |
771 | - default_color_job) |
772 | - |
773 | - parser.set_defaults(color_emits=default_color_emits, |
774 | - color_start_on=default_color_start_on, |
775 | - color_stop_on=default_color_stop_on, |
776 | - color_event=default_color_event, |
777 | - color_job=default_color_job, |
778 | - color_job_text=default_color_text, |
779 | - color_event_text=default_color_text, |
780 | - color_text=default_color_text, |
781 | - color_bg=default_color_bg, |
782 | - outfile=default_outfile) |
783 | - |
784 | - (options, args) = parser.parse_args() |
785 | - |
786 | - if options.outfile == '-': |
787 | - ofh = sys.stdout |
788 | - else: |
789 | - try: |
790 | - ofh = open(options.outfile, "w") |
791 | - except: |
792 | - sys.exit("ERROR: cannot open file %s for writing" % options.outfile) |
793 | - |
794 | - if options.restrictions: |
795 | - restrictions_list = options.restrictions.split(",") |
796 | - |
797 | - read_data() |
798 | - |
799 | - for job in restrictions_list: |
800 | - if not job in jobs: |
801 | - sys.exit("ERROR: unknown job %s" % job) |
802 | - |
803 | - header(ofh) |
804 | - show_events(ofh) |
805 | - show_jobs(ofh) |
806 | - show_edges(ofh) |
807 | - footer(ofh) |
808 | + global options |
809 | + global restrictions_list |
810 | + |
811 | + description = "Convert initctl(8) output to GraphViz dot(1) format." |
812 | + epilog = "See http://www.graphviz.org/doc/info/colors.html " \ |
813 | + "for available colours." |
814 | + |
815 | + parser = ArgumentParser(description=description, epilog=epilog) |
816 | + |
817 | + parser.add_argument("-r", "--restrict-to-jobs", |
818 | + dest="restrictions", |
819 | + help="Limit display of 'start on' and 'stop on' " |
820 | + "conditions to specified jobs (comma-separated list).") |
821 | + |
822 | + parser.add_argument("-f", "--infile", |
823 | + dest="infile", |
824 | + help="File to read '%s' output from. If not specified" |
825 | + ", initctl will be run automatically." % cmd) |
826 | + |
827 | + parser.add_argument("-o", "--outfile", |
828 | + dest="outfile", |
829 | + help="File to write output to (default=%s)" % |
830 | + default_outfile) |
831 | + |
832 | + parser.add_argument("--color-emits", |
833 | + dest="color_emits", |
834 | + help="Specify color for 'emits' lines (default=%s)." % |
835 | + default_color_emits) |
836 | + |
837 | + parser.add_argument("--color-start-on", |
838 | + dest="color_start_on", |
839 | + help="Specify color for 'start on' lines " |
840 | + "(default=%s)." % default_color_start_on) |
841 | + |
842 | + parser.add_argument("--color-stop-on", |
843 | + dest="color_stop_on", |
844 | + help="Specify color for 'stop on' lines " |
845 | + "(default=%s)." % default_color_stop_on) |
846 | + |
847 | + parser.add_argument("--color-event", |
848 | + dest="color_event", |
849 | + help="Specify color for event boxes (default=%s)." % |
850 | + default_color_event) |
851 | + |
852 | + parser.add_argument("--color-text", |
853 | + dest="color_text", |
854 | + help="Specify color for summary text (default=%s)." % |
855 | + default_color_text) |
856 | + |
857 | + parser.add_argument("--color-bg", |
858 | + dest="color_bg", |
859 | + help="Specify background color for diagram " |
860 | + "(default=%s)." % default_color_bg) |
861 | + |
862 | + parser.add_argument("--color-event-text", |
863 | + dest="color_event_text", |
864 | + help="Specify color for text in event boxes " |
865 | + "(default=%s)." % default_color_text) |
866 | + |
867 | + parser.add_argument("--color-job-text", |
868 | + dest="color_job_text", |
869 | + help="Specify color for text in job boxes " |
870 | + "(default=%s)." % default_color_text) |
871 | + |
872 | + parser.add_argument("--color-job", |
873 | + dest="color_job", |
874 | + help="Specify color for job boxes (default=%s)." % |
875 | + default_color_job) |
876 | + |
877 | + parser.set_defaults(color_emits=default_color_emits, |
878 | + color_start_on=default_color_start_on, |
879 | + color_stop_on=default_color_stop_on, |
880 | + color_event=default_color_event, |
881 | + color_job=default_color_job, |
882 | + color_job_text=default_color_text, |
883 | + color_event_text=default_color_text, |
884 | + color_text=default_color_text, |
885 | + color_bg=default_color_bg, |
886 | + outfile=default_outfile) |
887 | + |
888 | + options = parser.parse_args() |
889 | + |
890 | + if options.outfile == '-': |
891 | + ofh = sys.stdout |
892 | + else: |
893 | + try: |
894 | + ofh = open(options.outfile, "w") |
895 | + except: |
896 | + sys.exit("ERROR: cannot open file %s for writing" % |
897 | + options.outfile) |
898 | + |
899 | + if options.restrictions: |
900 | + restrictions_list = options.restrictions.split(",") |
901 | + |
902 | + read_data() |
903 | + |
904 | + for job in restrictions_list: |
905 | + if not job in jobs: |
906 | + sys.exit("ERROR: unknown job %s" % job) |
907 | + |
908 | + header(ofh) |
909 | + show_events(ofh) |
910 | + show_jobs(ofh) |
911 | + show_edges(ofh) |
912 | + footer(ofh) |
913 | |
914 | |
915 | if __name__ == "__main__": |
916 | - main() |
917 | + main() |
On Wed, Nov 28, 2012 at 05:10:25PM -0000, Stéphane Graber wrote: initctl2dot. py' initctl2dot. py 2011-06-06 12:52:08 +0000 initctl2dot. py 2012-11-28 17:09:22 +0000
> === modified file 'scripts/
> --- scripts/
> +++ scripts/
> @@ -50,522 +50,530 @@
> import re
> import fnmatch
> import os
> -from string import split
> import datetime
> from subprocess import (Popen, PIPE)
> from optparse import OptionParser
Shouldn't this also change the interpreter to /usr/bin/python3? I don't see
any reason why we would need this script to be bilingual, we ought to just
change it and go. If people want to use newer versions of upstart on OSes
that don't have python3, then they just won't be able to use this script...
but this is hardly a requirement anyway.
(Also, looking at this I notice we're shipping initctl2dot in /bin in the
Ubuntu package... that doesn't make much sense when the interpreter is in
/usr/bin.)