From c11d5ba2f132c1789cba35dd60d5f742d52d8af7 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Fri, 6 Mar 2026 17:05:38 +0100 Subject: [PATCH 1/2] [roottest] Make ruff happier with custom_diff.py --- roottest/scripts/custom_diff.py | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/roottest/scripts/custom_diff.py b/roottest/scripts/custom_diff.py index b08ba45fefc23..a2b0e6eb9705f 100755 --- a/roottest/scripts/custom_diff.py +++ b/roottest/scripts/custom_diff.py @@ -1,12 +1,18 @@ """ Clever (with filters) compare command using difflib.py providing diffs in four formats: - + * ndiff: lists every line and highlights interline changes. * context: highlights clusters of changes in a before/after format. * unified: highlights clusters of changes in an inline format. * html: generates side by side comparison with change highlights. - + """ -import sys, os, time, difflib, optparse, re +import difflib +import optparse +import os +import re +import sys +import time + #--------------------------------------------------------------------------------------------------------------------------- #---Filter and substitutions------------------------------------------------------------------------------------------------ @@ -67,16 +73,16 @@ def main(): parser.add_option("-n", action="store_true", default=False, help='Produce a ndiff format diff') parser.add_option("-l", "--lines", type="int", default=3, help='Set number of context lines (default 3)') (options, args) = parser.parse_args() - + if len(args) == 0: parser.print_help() sys.exit(1) if len(args) != 2: parser.error("need to specify both a fromfile and tofile") - + n = options.lines fromfile, tofile = args - + fromdate = time.ctime(os.stat(fromfile).st_mtime) todate = time.ctime(os.stat(tofile).st_mtime) if sys.platform == 'win32': @@ -91,7 +97,7 @@ def main(): check = difflib.context_diff(nows_fromlines, nows_tolines) try: - first = next(check) + _ = next(check) except StopIteration: sys.exit(0) @@ -106,12 +112,14 @@ def main(): diff = difflib.HtmlDiff().make_file(fromlines,tolines,fromfile,tofile,context=options.c,numlines=n) else: diff = difflib.context_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n) - + difflines = [line for line in diff] sys.stdout.writelines(difflines) - if difflines : sys.exit(1) - else : sys.exit(0) + if difflines: + sys.exit(1) + else: + sys.exit(0) if __name__ == '__main__': main() From 17c5256ab9bd404f5d04714d36f5688df425fcb9 Mon Sep 17 00:00:00 2001 From: Stephan Hageboeck Date: Tue, 10 Mar 2026 11:39:40 +0100 Subject: [PATCH 2/2] [roottest] Precompile regexes in custom_diff.py. Instead of specifying regexes lazily inside the line filter loop, precompile them by instantiating a filter class. In this way, adding a new filter or replacement can be done by adding a single line, and the regexes run a tiny bit faster. --- roottest/scripts/custom_diff.py | 133 ++++++++++++++++++-------------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/roottest/scripts/custom_diff.py b/roottest/scripts/custom_diff.py index a2b0e6eb9705f..10f5e1e9c97bf 100755 --- a/roottest/scripts/custom_diff.py +++ b/roottest/scripts/custom_diff.py @@ -1,11 +1,11 @@ -""" Clever (with filters) compare command using difflib.py providing diffs in four formats: +"""Clever (with filters) compare command using difflib.py providing diffs in four formats: - * ndiff: lists every line and highlights interline changes. - * context: highlights clusters of changes in a before/after format. - * unified: highlights clusters of changes in an inline format. - * html: generates side by side comparison with change highlights. +* ndiff: lists every line and highlights interline changes. +* context: highlights clusters of changes in a before/after format. +* unified: highlights clusters of changes in an inline format. +* html: generates side by side comparison with change highlights. - """ +""" import difflib import optparse import os @@ -13,55 +13,73 @@ import sys import time + #--------------------------------------------------------------------------------------------------------------------------- #---Filter and substitutions------------------------------------------------------------------------------------------------ - -def filter(lines, ignoreWhiteSpace = False): - outlines = [] - for line in lines: - if sys.platform == 'win32': - if 'Creating library ' in line: - continue - if '_ACLiC_dict' in line: - continue - if 'Warning in :' in line: - continue - if 'Warning in :' in line: - continue - if 'Error: Removing ' in line: - continue - if ' -nologo -TP -c -nologo -I' in line: - continue - if 'rootcling -v1 -f ' in line: - continue - if 'No precompiled header available' in line: - continue - #if line in ['\n', '\r\n']: - # continue - #---Processing line from interpreter (root.exe)------------------------------ - if re.match(r'^Processing ', line): - continue - #---ACLiC info--------------------------------------------------------------- - if re.match(r'^Info in <\w+::ACLiC>: creating shared library', line): - continue - #---Compilation error-------------------------------------------------------- - elif re.search(r': error:', line): - nline = re.sub(r'\S+/', '', line) - nline = re.sub(r'(:|_)[0-9]+(?=:)', ':--', nline) - #---Wrapper input line------------------------------------------------------- - elif re.match(r'^In file included from input_line', line): - continue - else: - nline = line - #---Remove Addresses in cling/cint------------------------------------------- - nline = re.sub(r'[ ]@0x[a-fA-F0-9]+', '', nline) - #---Remove versioning in std------------------------------------------------- - nline = re.sub(r'std::__[0-9]::', 'std::', nline) - #---Remove white spaces------------------------------------------------------ - if (ignoreWhiteSpace): - nline = re.sub(r'[ ]', '', nline) - outlines.append(nline) - return outlines +class LineFilter: + """A line filter to suppress lines in the diff. + self.skip_regexes contains patterns to skip entirely. + self.substitutions contains patterns to replace with the given strings. + These patterns only need to be compiled once for all the incoming lines. + """ + + def __init__(self): + # Skip these lines + self.skip_regexes = [ + re.compile(pattern) + for pattern in [ + r"^Processing ", # Interpreted macros + r"^Info in <\w+::ACLiC>: creating shared library", # Compiled macros + r"^In file included from input_line", # Wrapper input line + r"^[:space:]*$", # Lines which are empty apart from spaces + ] + ] + + # Replace these patterns in all lines + self.substitutions = [ + (re.compile(r"[ ]@0x[a-fA-F0-9]+"), ""), # Remove pointers from output + (re.compile(r"std::__[0-9]+::"), "std::"), # Canonicalise standard namespaces + ( + re.compile(r"^(\S*/|)([^:/]+)[-:0-9]*(?=: error:)"), + r"\2", + ), # Trim file paths and line numbers from lines with ": error:" + (re.compile(r"(input_line_|ROOT_prompt_)[0-9]+"), r"\1--"), # Canonicalise input_line_123, ROOT_prompt_345 + ] + + def filter(self, lines, ignoreWhiteSpace=False): + outlines = [] + for line in lines: + if sys.platform == "win32": + if "Creating library " in line: + continue + if "_ACLiC_dict" in line: + continue + if "Warning in :" in line: + continue + if "Warning in :" in line: + continue + if "Error: Removing " in line: + continue + if " -nologo -TP -c -nologo -I" in line: + continue + if "rootcling -v1 -f " in line: + continue + if "No precompiled header available" in line: + continue + + # ---Skip all lines matching predefined expressions--------------------------- + if any(pattern.match(line) is not None for pattern in self.skip_regexes): + continue + + # ---Apply predefined replacements-------------------------------------------- + for pattern, replacement in self.substitutions: + line = pattern.sub(replacement, line) + + # ---Remove white spaces------------------------------------------------------ + if ignoreWhiteSpace: + line = re.sub(r"[ ]", "", line) + outlines.append(line) + return outlines #----------------------------------------------------------------------------------------------------------------------------- def main(): @@ -92,8 +110,9 @@ def main(): fromlines = open(fromfile, 'r' if sys.version_info >= (3, 4) else 'U').readlines() tolines = open(tofile, 'r' if sys.version_info >= (3, 4) else 'U').readlines() - nows_fromlines = filter(fromlines, True) - nows_tolines = filter(tolines, True) + lineFilter = LineFilter() + nows_fromlines = lineFilter.filter(fromlines, True) + nows_tolines = lineFilter.filter(tolines, True) check = difflib.context_diff(nows_fromlines, nows_tolines) try: @@ -101,8 +120,8 @@ def main(): except StopIteration: sys.exit(0) - fromlines = filter(fromlines, False) - tolines = filter(tolines, False) + fromlines = lineFilter.filter(fromlines, False) + tolines = lineFilter.filter(tolines, False) if options.u: diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile, fromdate, todate, n=n)