hb_test_tools.py 7.3 KB
Newer Older
1 2
#!/usr/bin/python

B
Behdad Esfahbod 已提交
3
import sys, os, re, difflib, unicodedata, errno
B
Behdad Esfahbod 已提交
4
from itertools import *
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

class Colors:
	class Null:
		red = ''
		green = ''
		end = ''
	class ANSI:
		red = '\033[41;37;1m'
		green = '\033[42;37;1m'
		end = '\033[m'
	class HTML:
		red = '<span style="color:red">'
		green = '<span style="color:green">'
		end = '</span>'

	@staticmethod
	def Auto (argv = [], out = sys.stdout):
B
Behdad Esfahbod 已提交
22 23
		if os.isatty (out.fileno ()):
			color = Colors.ANSI
24
		else:
B
Behdad Esfahbod 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
			color = Colors.Null
		if "--color" in argv:
			argv.remove ("--color")
			color = Colors.ANSI
		if "--color=ansi" in argv:
			argv.remove ("--color=ansi")
			color = Colors.ANSI
		if "--color=html" in argv:
			argv.remove ("--color=html")
			color = Colors.HTML
		if "--no-color" in argv:
			argv.remove ("--no-color")
			color = Colors.Null
		return color

40

B
Behdad Esfahbod 已提交
41 42 43 44
	@staticmethod
	def Default (argv = []):
		return Colors.ANSI

45 46 47 48 49 50 51 52

class FancyDiffer:

	diff_regex = re.compile ('([a-za-z0-9_]*)([^a-za-z0-9_]?)')

	@staticmethod
	def diff_lines (l1, l2, colors=Colors.Null):

B
Behdad Esfahbod 已提交
53 54 55 56 57 58
		# Easy without colors
		if colors == Colors.Null:
			if l1 == l2:
				return [' ', l1]
			return ['-', l1, '+', l2]

59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
		ss = [FancyDiffer.diff_regex.sub (r'\1\n\2\n', l).splitlines (True) for l in (l1, l2)]
		oo = ["",""]
		st = [False, False]
		for l in difflib.Differ().compare (*ss):
			if l[0] == '?':
				continue
			if l[0] == ' ':
				for i in range(2):
					if st[i]:
						oo[i] += colors.end
						st[i] = False
				oo = [o + l[2:] for o in oo]
				continue
			if l[0] == '-':
				if not st[0]:
					oo[0] += colors.red
					st[0] = True
				oo[0] += l[2:]
				continue
			if l[0] == '+':
				if not st[1]:
					oo[1] += colors.green
					st[1] = True
				oo[1] += l[2:]
		for i in range(2):
			if st[i]:
				oo[i] += colors.end
				st[i] = 0
		oo = [o.replace ('\n', '') for o in oo]
		if oo[0] == oo[1]:
			return [' ', oo[0], '\n']
		return ['-', oo[0], '\n', '+', oo[1], '\n']

	@staticmethod
	def diff_files (f1, f2, colors=Colors.Null):
B
Behdad Esfahbod 已提交
94
		try:
B
Behdad Esfahbod 已提交
95
			for (l1,l2) in izip (f1, f2):
B
Behdad Esfahbod 已提交
96 97 98 99 100 101 102
				if l1 == l2:
					sys.stdout.writelines ([" ", l1])
					continue

				sys.stdout.writelines (FancyDiffer.diff_lines (l1, l2, colors))
			# print out residues
			for l in f1:
B
Behdad Esfahbod 已提交
103
				sys.stdout.writelines (["-", colors.red, l, colors.end])
B
Behdad Esfahbod 已提交
104
			for l in f2:
B
Behdad Esfahbod 已提交
105
				sys.stdout.writelines (["-", colors.green, l, colors.end])
B
Behdad Esfahbod 已提交
106 107
		except IOError as e:
			if e.errno != errno.EPIPE:
108
				print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
B
Behdad Esfahbod 已提交
109
				sys.exit (1)
110

B
Behdad Esfahbod 已提交
111

112 113 114 115 116 117
class DiffFilters:

	@staticmethod
	def filter_failures (f):
		for l in f:
			if l[0] in '-+':
B
Behdad Esfahbod 已提交
118
				# TODO retain all lines of the failure
B
Behdad Esfahbod 已提交
119
				yield l
120 121


B
Behdad Esfahbod 已提交
122 123
class ShapeFilters:

B
Behdad Esfahbod 已提交
124
	pass
B
Behdad Esfahbod 已提交
125 126


B
Behdad Esfahbod 已提交
127
class FilterHelpers:
128

B
Cleanup  
Behdad Esfahbod 已提交
129
	@staticmethod
B
Behdad Esfahbod 已提交
130
	def filter_printer_function (filter_callback):
B
Cleanup  
Behdad Esfahbod 已提交
131
		def printer (f):
B
Behdad Esfahbod 已提交
132
			for line in filter_callback (f):
B
Cleanup  
Behdad Esfahbod 已提交
133 134 135
				print line
		return printer

B
Behdad Esfahbod 已提交
136 137 138 139 140 141 142 143 144
	@staticmethod
	def filter_printer_function_no_newline (filter_callback):
		def printer (f):
			for line in filter_callback (f):
				sys.stdout.writelines ([line])
		return printer


class UtilMains:
B
Cleanup  
Behdad Esfahbod 已提交
145

146
	@staticmethod
147
	def process_multiple_files (callback, mnemonic = "FILE"):
148 149

		if len (sys.argv) == 1:
150
			print "Usage: %s %s..." % (sys.argv[0], mnemonic)
151 152
			sys.exit (1)

B
Behdad Esfahbod 已提交
153 154 155 156 157
		try:
			for s in sys.argv[1:]:
				callback (FileHelpers.open_file_or_stdin (s))
		except IOError as e:
			if e.errno != errno.EPIPE:
158
				print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
B
Behdad Esfahbod 已提交
159
				sys.exit (1)
160 161

	@staticmethod
162 163 164 165 166 167
	def process_multiple_args (callback, mnemonic):

		if len (sys.argv) == 1:
			print "Usage: %s %s..." % (sys.argv[0], mnemonic)
			sys.exit (1)

B
Behdad Esfahbod 已提交
168 169 170 171 172
		try:
			for s in sys.argv[1:]:
				callback (s)
		except IOError as e:
			if e.errno != errno.EPIPE:
173
				print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
B
Behdad Esfahbod 已提交
174
				sys.exit (1)
175 176 177

	@staticmethod
	def filter_multiple_strings_or_stdin (callback, mnemonic, \
178 179 180 181 182
					      separator = " ", \
					      concat_separator = False):

		if len (sys.argv) == 1 or ('--stdin' in sys.argv and len (sys.argv) != 2):
			print "Usage:\n  %s %s...\nor:\n  %s --stdin" \
183
			      % (sys.argv[0], mnemonic, sys.argv[0])
184 185
			sys.exit (1)

B
Behdad Esfahbod 已提交
186 187 188 189 190 191 192
		try:
			if '--stdin' in sys.argv:
				sys.argv.remove ('--stdin')
				while (1):
					line = sys.stdin.readline ()
					if not len (line):
						break
B
Behdad Esfahbod 已提交
193 194
					if line[-1] == '\n':
						line = line[:-1]
B
Behdad Esfahbod 已提交
195 196 197 198 199 200 201 202
					print callback (line)
			else:
				args = sys.argv[1:]
				if concat_separator != False:
					args = [concat_separator.join (args)]
				print separator.join (callback (x) for x in (args))
		except IOError as e:
			if e.errno != errno.EPIPE:
203
				print >> sys.stderr, "%s: %s: %s" % (sys.argv[0], e.filename, e.strerror)
B
Behdad Esfahbod 已提交
204
				sys.exit (1)
205 206 207 208 209 210


class Unicode:

	@staticmethod
	def decode (s):
B
Behdad Esfahbod 已提交
211
		return '<' + u','.join ("U+%04X" % ord (u) for u in unicode (s, 'utf-8')).encode ('utf-8') + '>'
212 213 214

	@staticmethod
	def encode (s):
B
Behdad Esfahbod 已提交
215
		s = re.sub (r"[<+>,\\uU\n	]", " ", s)
216
		s = re.sub (r"0[xX]", " ", s)
B
Behdad Esfahbod 已提交
217
		return u''.join (unichr (int (x, 16)) for x in s.split (' ') if len (x)).encode ('utf-8')
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253

	shorthands = {
		"ZERO WIDTH NON-JOINER": "ZWNJ",
		"ZERO WIDTH JOINER": "ZWJ",
		"NARROW NO-BREAK SPACE": "NNBSP",
		"COMBINING GRAPHEME JOINER": "CGJ",
		"LEFT-TO-RIGHT MARK": "LRM",
		"RIGHT-TO-LEFT MARK": "RLM",
		"LEFT-TO-RIGHT EMBEDDING": "LRE",
		"RIGHT-TO-LEFT EMBEDDING": "RLE",
		"POP DIRECTIONAL FORMATTING": "PDF",
		"LEFT-TO-RIGHT OVERRIDE": "LRO",
		"RIGHT-TO-LEFT OVERRIDE": "RLO",
	}

	@staticmethod
	def pretty_name (u):
		try:
			s = unicodedata.name (u)
		except ValueError:
			return "XXX"
		s = re.sub (".* LETTER ", "", s)
		s = re.sub (".* VOWEL SIGN (.*)", r"\1-MATRA", s)
		s = re.sub (".* SIGN ", "", s)
		s = re.sub (".* COMBINING ", "", s)
		if re.match (".* VIRAMA", s):
			s = "HALANT"
		if s in Unicode.shorthands:
			s = Unicode.shorthands[s]
		return s

	@staticmethod
	def pretty_names (s):
		s = re.sub (r"[<+>\\uU]", " ", s)
		s = re.sub (r"0[xX]", " ", s)
		s = [unichr (int (x, 16)) for x in re.split ('[, \n]', s) if len (x)]
B
Behdad Esfahbod 已提交
254
		return u' + '.join (Unicode.pretty_name (x) for x in s).encode ('utf-8')
255

B
Behdad Esfahbod 已提交
256

257
class FileHelpers:
B
Behdad Esfahbod 已提交
258 259 260 261 262 263

	@staticmethod
	def open_file_or_stdin (f):
		if f == '-':
			return sys.stdin
		return file (f)
264

265 266 267 268

class Manifest:

	@staticmethod
B
Behdad Esfahbod 已提交
269 270
	def read (s, strict = True):

271 272
		if not os.path.exists (s):
			if strict:
B
Behdad Esfahbod 已提交
273
				print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], s)
274 275 276
				sys.exit (1)
			return

B
Behdad Esfahbod 已提交
277
		s = os.path.normpath (s)
278

B
Behdad Esfahbod 已提交
279
		if os.path.isdir (s):
280 281

			try:
282
				m = file (os.path.join (s, "MANIFEST"))
283 284
				items = [x.strip () for x in m.readlines ()]
				for f in items:
B
Behdad Esfahbod 已提交
285 286
					for p in Manifest.read (os.path.join (s, f)):
						yield p
287 288
			except IOError:
				if strict:
B
Behdad Esfahbod 已提交
289
					print >> sys.stderr, "%s: %s does not exist" % (sys.argv[0], os.path.join (s, "MANIFEST"))
290 291 292
					sys.exit (1)
				return
		else:
B
Behdad Esfahbod 已提交
293 294
			yield s

295 296 297 298 299
	@staticmethod
	def update_recursive (s):

		for dirpath, dirnames, filenames in os.walk (s, followlinks=True):

300
			for f in ["MANIFEST", "README", "LICENSE", "COPYING", "AUTHORS", "SOURCES", "ChangeLog"]:
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316
				if f in dirnames:
					dirnames.remove (f)
				if f in filenames:
					filenames.remove (f)
			dirnames.sort ()
			filenames.sort ()
			ms = os.path.join (dirpath, "MANIFEST")
			print "  GEN    %s" % ms
			m = open (ms, "w")
			for f in filenames:
				print >> m, f
			for f in dirnames:
				print >> m, f
			for f in dirnames:
				Manifest.update_recursive (os.path.join (dirpath, f))

317 318
if __name__ == '__main__':
	pass