From aab5d176b83fe8d5bbf5502c38c4aa0a8558a22f Mon Sep 17 00:00:00 2001 From: Matti Hamalainen Date: Mon, 20 Jun 2022 14:20:09 +0300 Subject: [PATCH] pytracediff: implement pager ('less') invocation internally In order to get rid of the ntracediff.sh wrapper script, implement invocation of 'less' internally, if the stdout is determined to be a tty. Otherwise just print out normally. Signed-off-by: Matti Hamalainen Acked-by: Mike Blumenkrantz Reviewed-by: Dylan Baker Part-of: --- src/gallium/tools/trace/pytracediff.py | 236 +++++++++++++++++++-------------- 1 file changed, 135 insertions(+), 101 deletions(-) diff --git a/src/gallium/tools/trace/pytracediff.py b/src/gallium/tools/trace/pytracediff.py index 7c79f75..15ea36f 100755 --- a/src/gallium/tools/trace/pytracediff.py +++ b/src/gallium/tools/trace/pytracediff.py @@ -26,12 +26,14 @@ ########################################################################## from parse import * +import os import sys import re import signal import functools import argparse import difflib +import subprocess assert sys.version_info >= (3, 6), 'Python >= 3.6 required' @@ -54,6 +56,8 @@ PKK_ANSI_ITALIC = '3m' ### def pkk_fatal(msg): print(f"ERROR: {msg}", file=sys.stderr) + if outpipe is not None: + outpipe.terminate() sys.exit(1) @@ -61,8 +65,17 @@ def pkk_info(msg): print(msg, file=sys.stderr) +def pkk_output(outpipe, msg): + if outpipe is not None: + print(msg, file=outpipe.stdin) + else: + print(msg) + + def pkk_signal_handler(signal, frame): print("\nQuitting due to SIGINT / Ctrl+C!") + if outpipe is not None: + outpipe.terminate() sys.exit(1) @@ -259,6 +272,16 @@ def pkk_format_line(line, indent, width): ### Main program starts ### if __name__ == "__main__": + ### Check if output is a terminal + outpipe = None + redirect = False + + try: + defwidth = os.get_terminal_size().columns + redirect = True + except OSError: + defwidth = 80 + signal.signal(signal.SIGINT, pkk_signal_handler) ### Parse arguments @@ -307,7 +330,7 @@ if __name__ == "__main__": optparser.add_argument("-w", "--width", dest="output_width", - type=functools.partial(pkk_arg_range, vmin=16, vmax=512), default=80, + type=functools.partial(pkk_arg_range, vmin=16, vmax=512), default=defwidth, metavar="N", help="output width (default: %(default)s)") @@ -327,115 +350,126 @@ if __name__ == "__main__": print("The files are identical.") sys.exit(0) - ### Output results - pkk_info("Outputting diff ...") - colwidth = int((options.output_width - 3) / 2) - colfmt = "{}{:"+ str(colwidth) +"s}{} {}{}{} {}{:"+ str(colwidth) +"s}{}" - - printer = PKKPrettyPrinter(options) - - prevtag = "" - for tag, start1, end1, start2, end2 in opcodes: - if tag == "equal": - show_args = False - if options.suppress_common: - if tag != prevtag: - print("[...]") - continue - - sep = "|" - ansi1 = ansi2 = ansiend = "" - show_args = False - elif tag == "insert": - sep = "+" - ansi1 = "" - ansi2 = PKK_ANSI_ESC + PKK_ANSI_GREEN - show_args = True - elif tag == "delete": - sep = "-" - ansi1 = PKK_ANSI_ESC + PKK_ANSI_RED - ansi2 = "" - show_args = True - elif tag == "replace": - sep = ">" - ansi1 = ansi2 = PKK_ANSI_ESC + PKK_ANSI_BOLD - show_args = True - else: - pkk_fatal(f"Internal error, unsupported difflib.SequenceMatcher operation '{tag}'.") + ### Redirect output to 'less' if stdout is a tty + try: + if redirect: + outpipe = subprocess.Popen(["less", "-S", "-R"], stdin=subprocess.PIPE, encoding="utf8") + + ### Output results + pkk_info("Outputting diff ...") + colwidth = int((options.output_width - 3) / 2) + colfmt = "{}{:"+ str(colwidth) +"s}{} {}{}{} {}{:"+ str(colwidth) +"s}{}" + + printer = PKKPrettyPrinter(options) + + prevtag = "" + for tag, start1, end1, start2, end2 in opcodes: + if tag == "equal": + show_args = False + if options.suppress_common: + if tag != prevtag: + pkk_output(outpipe, "[...]") + continue + + sep = "|" + ansi1 = ansi2 = ansiend = "" + show_args = False + elif tag == "insert": + sep = "+" + ansi1 = "" + ansi2 = PKK_ANSI_ESC + PKK_ANSI_GREEN + show_args = True + elif tag == "delete": + sep = "-" + ansi1 = PKK_ANSI_ESC + PKK_ANSI_RED + ansi2 = "" + show_args = True + elif tag == "replace": + sep = ">" + ansi1 = ansi2 = PKK_ANSI_ESC + PKK_ANSI_BOLD + show_args = True + else: + pkk_fatal(f"Internal error, unsupported difflib.SequenceMatcher operation '{tag}'.") - # No ANSI, please - if options.plain: - ansi1 = ansisep = ansi2 = ansiend = "" - else: - ansisep = PKK_ANSI_ESC + PKK_ANSI_PURPLE - ansiend = PKK_ANSI_ESC + PKK_ANSI_NORMAL - - - # Print out the block - ncall1 = start1 - ncall2 = start2 - last1 = last2 = False - while True: - # Get line data - if ncall1 < end1: - if not options.ignore_junk or not stack1[ncall1].is_junk: - printer.entry_start(show_args) - stack1[ncall1].visit(printer) - data1 = printer.entry_get() + # No ANSI, please + if options.plain: + ansi1 = ansisep = ansi2 = ansiend = "" + else: + ansisep = PKK_ANSI_ESC + PKK_ANSI_PURPLE + ansiend = PKK_ANSI_ESC + PKK_ANSI_NORMAL + + + # Print out the block + ncall1 = start1 + ncall2 = start2 + last1 = last2 = False + while True: + # Get line data + if ncall1 < end1: + if not options.ignore_junk or not stack1[ncall1].is_junk: + printer.entry_start(show_args) + stack1[ncall1].visit(printer) + data1 = printer.entry_get() + else: + data1 = [] + ncall1 += 1 else: data1 = [] - ncall1 += 1 - else: - data1 = [] - last1 = True - - if ncall2 < end2: - if not options.ignore_junk or not stack2[ncall2].is_junk: - printer.entry_start(show_args) - stack2[ncall2].visit(printer) - data2 = printer.entry_get() + last1 = True + + if ncall2 < end2: + if not options.ignore_junk or not stack2[ncall2].is_junk: + printer.entry_start(show_args) + stack2[ncall2].visit(printer) + data2 = printer.entry_get() + else: + data2 = [] + ncall2 += 1 else: data2 = [] - ncall2 += 1 - else: - data2 = [] - last2 = True - - # Check if we are at last call of both - if last1 and last2: - break - - nline = 0 - while nline < len(data1) or nline < len(data2): - # Determine line start indentation - if nline > 0: - if options.suppress_variants: - indent = " "*8 + last2 = True + + # Check if we are at last call of both + if last1 and last2: + break + + nline = 0 + while nline < len(data1) or nline < len(data2): + # Determine line start indentation + if nline > 0: + if options.suppress_variants: + indent = " "*8 + else: + indent = " "*12 else: - indent = " "*12 - else: - indent = "" + indent = "" + + line1 = pkk_get_line(data1, nline) + line2 = pkk_get_line(data2, nline) + + # Highlight differing lines if not plain + if not options.plain and line1 != line2: + if tag == "insert" or tag == "delete": + ansi1 = ansi1 + PKK_ANSI_ESC + PKK_ANSI_BOLD + elif tag == "replace": + ansi1 = ansi2 = ansi1 + PKK_ANSI_ESC + PKK_ANSI_YELLOW - line1 = pkk_get_line(data1, nline) - line2 = pkk_get_line(data2, nline) + # Output line + pkk_output(outpipe, colfmt.format( + ansi1, pkk_format_line(line1, indent, colwidth), ansiend, + ansisep, sep, ansiend, + ansi2, pkk_format_line(line2, indent, colwidth), ansiend). + rstrip()) - # Highlight differing lines if not plain - if not options.plain and line1 != line2: - if tag == "insert" or tag == "delete": - ansi1 = ansi1 + PKK_ANSI_ESC + PKK_ANSI_BOLD - elif tag == "replace": - ansi1 = ansi2 = ansi1 + PKK_ANSI_ESC + PKK_ANSI_YELLOW + nline += 1 - # Output line - print(colfmt.format( - ansi1, pkk_format_line(line1, indent, colwidth), ansiend, - ansisep, sep, ansiend, - ansi2, pkk_format_line(line2, indent, colwidth), ansiend). - rstrip()) + if tag == "equal" and options.suppress_common: + pkk_output(outpipe, "[...]") - nline += 1 + prevtag = tag - if tag == "equal" and options.suppress_common: - print("[...]") + except Exception as e: + pkk_fatal(str(e)) - prevtag = tag + if outpipe is not None: + outpipe.communicate() -- 2.7.4