blob: ba4a8bb7bbdca62ad5c50ac4ca5ee67de08ea5da [file] [log] [blame]
Behdad Esfahbod98687492015-04-06 14:51:31 -07001#!/usr/bin/env python
Behdad Esfahbod91540a72012-01-20 18:27:52 -05002
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +04303from __future__ import print_function
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +02004import sys, os, re, difflib, unicodedata, errno, cgi
Behdad Esfahbod7d221352012-05-08 19:38:49 +02005from itertools import *
Behdad Esfahbod91540a72012-01-20 18:27:52 -05006
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +02007diff_symbols = "-+=*&^%$#@!~/"
8diff_colors = ['red', 'green', 'blue']
9
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +043010if sys.version_info[0] >= 3:
11 unichr = chr
12
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020013class ColorFormatter:
14
Behdad Esfahbod91540a72012-01-20 18:27:52 -050015 class Null:
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020016 @staticmethod
17 def start_color (c): return ''
18 @staticmethod
19 def end_color (): return ''
20 @staticmethod
21 def escape (s): return s
22 @staticmethod
23 def newline (): return '\n'
24
Behdad Esfahbod91540a72012-01-20 18:27:52 -050025 class ANSI:
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020026 @staticmethod
27 def start_color (c):
28 return {
29 'red': '\033[41;37;1m',
30 'green': '\033[42;37;1m',
31 'blue': '\033[44;37;1m',
32 }[c]
33 @staticmethod
34 def end_color ():
35 return '\033[m'
36 @staticmethod
37 def escape (s): return s
38 @staticmethod
39 def newline (): return '\n'
40
Behdad Esfahbod91540a72012-01-20 18:27:52 -050041 class HTML:
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020042 @staticmethod
43 def start_color (c):
44 return '<span style="background:%s">' % c
45 @staticmethod
46 def end_color ():
47 return '</span>'
48 @staticmethod
49 def escape (s): return cgi.escape (s)
50 @staticmethod
51 def newline (): return '<br/>\n'
Behdad Esfahbod91540a72012-01-20 18:27:52 -050052
53 @staticmethod
54 def Auto (argv = [], out = sys.stdout):
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020055 format = ColorFormatter.ANSI
56 if "--format" in argv:
57 argv.remove ("--format")
58 format = ColorFormatter.ANSI
59 if "--format=ansi" in argv:
60 argv.remove ("--format=ansi")
61 format = ColorFormatter.ANSI
62 if "--format=html" in argv:
63 argv.remove ("--format=html")
64 format = ColorFormatter.HTML
65 if "--no-format" in argv:
66 argv.remove ("--no-format")
67 format = ColorFormatter.Null
68 return format
Behdad Esfahbode4ccbfe2012-01-22 16:07:32 -050069
Behdad Esfahbod91540a72012-01-20 18:27:52 -050070
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020071class DiffColorizer:
Behdad Esfahbod91540a72012-01-20 18:27:52 -050072
73 diff_regex = re.compile ('([a-za-z0-9_]*)([^a-za-z0-9_]?)')
74
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020075 def __init__ (self, formatter, colors=diff_colors, symbols=diff_symbols):
76 self.formatter = formatter
77 self.colors = colors
78 self.symbols = symbols
Behdad Esfahbod91540a72012-01-20 18:27:52 -050079
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020080 def colorize_lines (self, lines):
81 lines = (l if l else '' for l in lines)
82 ss = [self.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in lines]
Behdad Esfahbod91540a72012-01-20 18:27:52 -050083 oo = ["",""]
84 st = [False, False]
85 for l in difflib.Differ().compare (*ss):
86 if l[0] == '?':
87 continue
88 if l[0] == ' ':
89 for i in range(2):
90 if st[i]:
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020091 oo[i] += self.formatter.end_color ()
Behdad Esfahbod91540a72012-01-20 18:27:52 -050092 st[i] = False
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020093 oo = [o + self.formatter.escape (l[2:]) for o in oo]
Behdad Esfahbod91540a72012-01-20 18:27:52 -050094 continue
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +020095 if l[0] in self.symbols:
96 i = self.symbols.index (l[0])
97 if not st[i]:
98 oo[i] += self.formatter.start_color (self.colors[i])
99 st[i] = True
100 oo[i] += self.formatter.escape (l[2:])
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500101 continue
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500102 for i in range(2):
103 if st[i]:
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +0200104 oo[i] += self.formatter.end_color ()
105 st[i] = False
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500106 oo = [o.replace ('\n', '') for o in oo]
Behdad Esfahbodf1eb0082012-05-08 23:41:41 +0200107 return [s1+s2+self.formatter.newline () for (s1,s2) in zip (self.symbols, oo) if s2]
108
109 def colorize_diff (self, f):
110 lines = [None, None]
111 for l in f:
112 if l[0] not in self.symbols:
113 yield self.formatter.escape (l).replace ('\n', self.formatter.newline ())
114 continue
115 i = self.symbols.index (l[0])
116 if lines[i]:
117 # Flush
118 for line in self.colorize_lines (lines):
119 yield line
120 lines = [None, None]
121 lines[i] = l[1:]
122 if (all (lines)):
123 # Flush
124 for line in self.colorize_lines (lines):
125 yield line
126 lines = [None, None]
127 if (any (lines)):
128 # Flush
129 for line in self.colorize_lines (lines):
130 yield line
131
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500132
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +0200133class ZipDiffer:
134
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500135 @staticmethod
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +0200136 def diff_files (files, symbols=diff_symbols):
137 files = tuple (files) # in case it's a generator, copy it
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500138 try:
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +0200139 for lines in izip_longest (*files):
140 if all (lines[0] == line for line in lines[1:]):
141 sys.stdout.writelines ([" ", lines[0]])
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500142 continue
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500143
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +0200144 for i, l in enumerate (lines):
145 if l:
146 sys.stdout.writelines ([symbols[i], l])
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500147 except IOError as e:
148 if e.errno != errno.EPIPE:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430149 print ("%s: %s: %s" % (sys.argv[0], e.filename, e.strerror), file=sys.stderr)
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500150 sys.exit (1)
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500151
Behdad Esfahbodad34e392012-01-20 18:39:27 -0500152
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500153class DiffFilters:
154
155 @staticmethod
Behdad Esfahbod1058d032012-05-09 07:30:07 +0200156 def filter_failures (f):
Behdad Esfahbod98669ce2012-05-09 08:16:15 +0200157 for key, lines in DiffHelpers.separate_test_cases (f):
158 lines = list (lines)
Behdad Esfahbodc438a142012-05-09 07:45:17 +0200159 if not DiffHelpers.test_passed (lines):
Behdad Esfahbod1058d032012-05-09 07:30:07 +0200160 for l in lines: yield l
161
Behdad Esfahbod2214a032012-05-09 09:54:54 +0200162class Stat:
163
164 def __init__ (self):
165 self.count = 0
166 self.freq = 0
167
168 def add (self, test):
169 self.count += 1
170 self.freq += test.freq
171
172class Stats:
173
174 def __init__ (self):
175 self.passed = Stat ()
176 self.failed = Stat ()
177 self.total = Stat ()
178
179 def add (self, test):
180 self.total.add (test)
181 if test.passed:
182 self.passed.add (test)
183 else:
184 self.failed.add (test)
185
186 def mean (self):
187 return float (self.passed.count) / self.total.count
188
189 def variance (self):
190 return (float (self.passed.count) / self.total.count) * \
191 (float (self.failed.count) / self.total.count)
192
193 def stddev (self):
194 return self.variance () ** .5
195
196 def zscore (self, population):
197 """Calculate the standard score.
198 Population is the Stats for population.
199 Self is Stats for sample.
200 Returns larger absolute value if sample is highly unlikely to be random.
201 Anything outside of -3..+3 is very unlikely to be random.
202 See: http://en.wikipedia.org/wiki/Standard_score"""
203
204 return (self.mean () - population.mean ()) / population.stddev ()
205
206
207
208
Behdad Esfahbodc438a142012-05-09 07:45:17 +0200209class DiffSinks:
210
211 @staticmethod
212 def print_stat (f):
213 passed = 0
214 failed = 0
Behdad Esfahbod2214a032012-05-09 09:54:54 +0200215 # XXX port to Stats, but that would really slow us down here
Behdad Esfahbod98669ce2012-05-09 08:16:15 +0200216 for key, lines in DiffHelpers.separate_test_cases (f):
Behdad Esfahbodc438a142012-05-09 07:45:17 +0200217 if DiffHelpers.test_passed (lines):
218 passed += 1
219 else:
220 failed += 1
221 total = passed + failed
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430222 print ("%d out of %d tests passed. %d failed (%g%%)" % (passed, total, failed, 100. * failed / total))
Behdad Esfahbodc438a142012-05-09 07:45:17 +0200223
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200224 @staticmethod
225 def print_ngrams (f, ns=(1,2,3)):
226 gens = tuple (Ngram.generator (n) for n in ns)
Behdad Esfahbod2214a032012-05-09 09:54:54 +0200227 allstats = Stats ()
228 allgrams = {}
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200229 for key, lines in DiffHelpers.separate_test_cases (f):
230 test = Test (lines)
Behdad Esfahbod2214a032012-05-09 09:54:54 +0200231 allstats.add (test)
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200232
233 for gen in gens:
Behdad Esfahbod2214a032012-05-09 09:54:54 +0200234 for ngram in gen (test.unicodes):
235 if ngram not in allgrams:
236 allgrams[ngram] = Stats ()
237 allgrams[ngram].add (test)
238
239 importantgrams = {}
240 for ngram, stats in allgrams.iteritems ():
241 if stats.failed.count >= 30: # for statistical reasons
242 importantgrams[ngram] = stats
243 allgrams = importantgrams
244 del importantgrams
245
246 for ngram, stats in allgrams.iteritems ():
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430247 print ("zscore: %9f failed: %6d passed: %6d ngram: <%s>" % (stats.zscore (allstats), stats.failed.count, stats.passed.count, ','.join ("U+%04X" % u for u in ngram)))
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200248
249
250
251class Test:
252
253 def __init__ (self, lines):
Behdad Esfahbod2214a032012-05-09 09:54:54 +0200254 self.freq = 1
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200255 self.passed = True
256 self.identifier = None
257 self.text = None
258 self.unicodes = None
259 self.glyphs = None
260 for l in lines:
261 symbol = l[0]
262 if symbol != ' ':
263 self.passed = False
264 i = 1
265 if ':' in l:
266 i = l.index (':')
267 if not self.identifier:
268 self.identifier = l[1:i]
269 i = i + 2 # Skip colon and space
270 j = -1
271 if l[j] == '\n':
272 j -= 1
273 brackets = l[i] + l[j]
274 l = l[i+1:-2]
275 if brackets == '()':
276 self.text = l
277 elif brackets == '<>':
278 self.unicodes = Unicode.parse (l)
279 elif brackets == '[]':
280 # XXX we don't handle failed tests here
281 self.glyphs = l
282
283
Behdad Esfahbod1058d032012-05-09 07:30:07 +0200284class DiffHelpers:
285
286 @staticmethod
287 def separate_test_cases (f):
288 '''Reads lines from f, and if the lines have identifiers, ie.
289 have a colon character, groups them by identifier,
290 yielding lists of all lines with the same identifier.'''
291
Behdad Esfahbod98669ce2012-05-09 08:16:15 +0200292 def identifier (l):
293 if ':' in l[1:]:
294 return l[1:l.index (':')]
295 return l
296 return groupby (f, key=identifier)
Behdad Esfahbodad34e392012-01-20 18:39:27 -0500297
Behdad Esfahbodc438a142012-05-09 07:45:17 +0200298 @staticmethod
299 def test_passed (lines):
Behdad Esfahboddeeb5402012-07-19 11:30:48 -0400300 lines = list (lines)
301 # XXX This is a hack, but does the job for now.
Behdad Esfahbod6b19fa42012-11-14 11:38:50 -0800302 if any (l.find("space+0|space+0") >= 0 for l in lines if l[0] == '+'): return True
Behdad Esfahbodefb8d3e2012-09-05 15:50:47 -0400303 if any (l.find("uni25CC") >= 0 for l in lines if l[0] == '+'): return True
304 if any (l.find("dottedcircle") >= 0 for l in lines if l[0] == '+'): return True
305 if any (l.find("glyph0") >= 0 for l in lines if l[0] == '+'): return True
Behdad Esfahbod911ed092012-10-29 19:42:19 -0700306 if any (l.find("gid0") >= 0 for l in lines if l[0] == '+'): return True
Behdad Esfahbodefb8d3e2012-09-05 15:50:47 -0400307 if any (l.find("notdef") >= 0 for l in lines if l[0] == '+'): return True
Behdad Esfahbodc438a142012-05-09 07:45:17 +0200308 return all (l[0] == ' ' for l in lines)
309
Behdad Esfahbodad34e392012-01-20 18:39:27 -0500310
Behdad Esfahbod8f80f932012-01-21 20:03:25 -0500311class FilterHelpers:
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500312
313 @staticmethod
Behdad Esfahbod8f80f932012-01-21 20:03:25 -0500314 def filter_printer_function (filter_callback):
Behdad Esfahbodc78c6e92012-01-21 19:55:16 -0500315 def printer (f):
Behdad Esfahbod8f80f932012-01-21 20:03:25 -0500316 for line in filter_callback (f):
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430317 print (line)
Behdad Esfahbodc78c6e92012-01-21 19:55:16 -0500318 return printer
319
Behdad Esfahbod8f80f932012-01-21 20:03:25 -0500320 @staticmethod
321 def filter_printer_function_no_newline (filter_callback):
322 def printer (f):
323 for line in filter_callback (f):
324 sys.stdout.writelines ([line])
325 return printer
326
327
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200328class Ngram:
329
330 @staticmethod
331 def generator (n):
332
333 def gen (f):
334 l = []
335 for x in f:
336 l.append (x)
337 if len (l) == n:
338 yield tuple (l)
339 l[:1] = []
340
341 gen.n = n
342 return gen
343
344
Behdad Esfahbod8f80f932012-01-21 20:03:25 -0500345class UtilMains:
Behdad Esfahbodc78c6e92012-01-21 19:55:16 -0500346
347 @staticmethod
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500348 def process_multiple_files (callback, mnemonic = "FILE"):
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500349
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +0200350 if "--help" in sys.argv:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430351 print ("Usage: %s %s..." % (sys.argv[0], mnemonic))
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500352 sys.exit (1)
353
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500354 try:
Behdad Esfahbod9155e4f2012-05-08 22:44:21 +0200355 files = sys.argv[1:] if len (sys.argv) > 1 else ['-']
356 for s in files:
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500357 callback (FileHelpers.open_file_or_stdin (s))
358 except IOError as e:
359 if e.errno != errno.EPIPE:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430360 print ("%s: %s: %s" % (sys.argv[0], e.filename, e.strerror), file=sys.stderr)
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500361 sys.exit (1)
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500362
363 @staticmethod
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500364 def process_multiple_args (callback, mnemonic):
365
Behdad Esfahbodf538fcb2012-05-12 15:34:40 +0200366 if len (sys.argv) == 1 or "--help" in sys.argv:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430367 print ("Usage: %s %s..." % (sys.argv[0], mnemonic))
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500368 sys.exit (1)
369
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500370 try:
371 for s in sys.argv[1:]:
372 callback (s)
373 except IOError as e:
374 if e.errno != errno.EPIPE:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430375 print ("%s: %s: %s" % (sys.argv[0], e.filename, e.strerror), file=sys.stderr)
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500376 sys.exit (1)
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500377
378 @staticmethod
379 def filter_multiple_strings_or_stdin (callback, mnemonic, \
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500380 separator = " ", \
381 concat_separator = False):
382
Behdad Esfahbodf538fcb2012-05-12 15:34:40 +0200383 if "--help" in sys.argv:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430384 print ("Usage:\n %s %s...\nor:\n %s\n\nWhen called with no arguments, input is read from standard input." \
385 % (sys.argv[0], mnemonic, sys.argv[0]))
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500386 sys.exit (1)
387
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500388 try:
Behdad Esfahbodf538fcb2012-05-12 15:34:40 +0200389 if len (sys.argv) == 1:
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500390 while (1):
391 line = sys.stdin.readline ()
392 if not len (line):
393 break
Behdad Esfahbod3c9a39e2012-01-22 16:21:19 -0500394 if line[-1] == '\n':
395 line = line[:-1]
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430396 print (callback (line))
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500397 else:
398 args = sys.argv[1:]
399 if concat_separator != False:
400 args = [concat_separator.join (args)]
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430401 print (separator.join (callback (x) for x in (args)))
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500402 except IOError as e:
403 if e.errno != errno.EPIPE:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430404 print ("%s: %s: %s" % (sys.argv[0], e.filename, e.strerror), file=sys.stderr)
Behdad Esfahbod3a34e9e2012-01-21 19:15:41 -0500405 sys.exit (1)
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500406
407
408class Unicode:
409
410 @staticmethod
411 def decode (s):
Behdad Esfahbode2dab692013-10-14 16:44:44 +0200412 return u','.join ("U+%04X" % ord (u) for u in unicode (s, 'utf-8')).encode ('utf-8')
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500413
414 @staticmethod
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200415 def parse (s):
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500416 s = re.sub (r"0[xX]", " ", s)
Behdad Esfahbod8f0a4d62015-04-23 14:32:33 -0700417 s = re.sub (r"[<+>,;&#\\xXuUnNiI\n ]", " ", s)
Behdad Esfahbodc4308f82014-08-13 19:42:01 -0400418 return [int (x, 16) for x in s.split ()]
Behdad Esfahbod178e6dc2012-05-09 08:57:29 +0200419
420 @staticmethod
421 def encode (s):
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430422 s = u''.join (unichr (x) for x in Unicode.parse (s))
423 if sys.version_info[0] == 2: s = s.encode ('utf-8')
424 return s
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500425
426 shorthands = {
427 "ZERO WIDTH NON-JOINER": "ZWNJ",
428 "ZERO WIDTH JOINER": "ZWJ",
429 "NARROW NO-BREAK SPACE": "NNBSP",
430 "COMBINING GRAPHEME JOINER": "CGJ",
431 "LEFT-TO-RIGHT MARK": "LRM",
432 "RIGHT-TO-LEFT MARK": "RLM",
433 "LEFT-TO-RIGHT EMBEDDING": "LRE",
434 "RIGHT-TO-LEFT EMBEDDING": "RLE",
435 "POP DIRECTIONAL FORMATTING": "PDF",
436 "LEFT-TO-RIGHT OVERRIDE": "LRO",
437 "RIGHT-TO-LEFT OVERRIDE": "RLO",
438 }
439
440 @staticmethod
441 def pretty_name (u):
442 try:
443 s = unicodedata.name (u)
444 except ValueError:
445 return "XXX"
446 s = re.sub (".* LETTER ", "", s)
447 s = re.sub (".* VOWEL SIGN (.*)", r"\1-MATRA", s)
448 s = re.sub (".* SIGN ", "", s)
449 s = re.sub (".* COMBINING ", "", s)
450 if re.match (".* VIRAMA", s):
451 s = "HALANT"
452 if s in Unicode.shorthands:
453 s = Unicode.shorthands[s]
454 return s
455
456 @staticmethod
457 def pretty_names (s):
458 s = re.sub (r"[<+>\\uU]", " ", s)
459 s = re.sub (r"0[xX]", " ", s)
460 s = [unichr (int (x, 16)) for x in re.split ('[, \n]', s) if len (x)]
Behdad Esfahbod46ac4562012-01-20 19:32:17 -0500461 return u' + '.join (Unicode.pretty_name (x) for x in s).encode ('utf-8')
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500462
Behdad Esfahbodad34e392012-01-20 18:39:27 -0500463
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500464class FileHelpers:
Behdad Esfahbodad34e392012-01-20 18:39:27 -0500465
466 @staticmethod
467 def open_file_or_stdin (f):
468 if f == '-':
469 return sys.stdin
470 return file (f)
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500471
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500472
473class Manifest:
474
475 @staticmethod
Behdad Esfahbod1e58df62012-01-21 19:37:31 -0500476 def read (s, strict = True):
477
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500478 if not os.path.exists (s):
479 if strict:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430480 print ("%s: %s does not exist" % (sys.argv[0], s), file=sys.stderr)
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500481 sys.exit (1)
482 return
483
Behdad Esfahbod1e58df62012-01-21 19:37:31 -0500484 s = os.path.normpath (s)
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500485
Behdad Esfahbod1e58df62012-01-21 19:37:31 -0500486 if os.path.isdir (s):
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500487
488 try:
Behdad Esfahbod956d5522012-01-21 19:31:51 -0500489 m = file (os.path.join (s, "MANIFEST"))
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500490 items = [x.strip () for x in m.readlines ()]
491 for f in items:
Behdad Esfahbod1e58df62012-01-21 19:37:31 -0500492 for p in Manifest.read (os.path.join (s, f)):
493 yield p
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500494 except IOError:
495 if strict:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430496 print ("%s: %s does not exist" % (sys.argv[0], os.path.join (s, "MANIFEST")), file=sys.stderr)
Behdad Esfahbod96968bf2012-01-20 21:16:34 -0500497 sys.exit (1)
498 return
499 else:
Behdad Esfahbod1e58df62012-01-21 19:37:31 -0500500 yield s
501
502 @staticmethod
Behdad Esfahbod956d5522012-01-21 19:31:51 -0500503 def update_recursive (s):
504
505 for dirpath, dirnames, filenames in os.walk (s, followlinks=True):
506
Behdad Esfahbod71be4ca2012-01-22 16:26:49 -0500507 for f in ["MANIFEST", "README", "LICENSE", "COPYING", "AUTHORS", "SOURCES", "ChangeLog"]:
Behdad Esfahbod956d5522012-01-21 19:31:51 -0500508 if f in dirnames:
509 dirnames.remove (f)
510 if f in filenames:
511 filenames.remove (f)
512 dirnames.sort ()
513 filenames.sort ()
514 ms = os.path.join (dirpath, "MANIFEST")
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430515 print (" GEN %s" % ms)
Behdad Esfahbod956d5522012-01-21 19:31:51 -0500516 m = open (ms, "w")
517 for f in filenames:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430518 print (f, file=m)
Behdad Esfahbod956d5522012-01-21 19:31:51 -0500519 for f in dirnames:
Ebrahim Byagowi363ceec2015-03-30 03:27:14 +0430520 print (f, file=m)
Behdad Esfahbod956d5522012-01-21 19:31:51 -0500521 for f in dirnames:
522 Manifest.update_recursive (os.path.join (dirpath, f))
523
Behdad Esfahbod91540a72012-01-20 18:27:52 -0500524if __name__ == '__main__':
525 pass