From e8bc67a6ee823d2b036a6792d58f2e9da2f5b798 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 10 Dec 2019 17:41:02 +0100 Subject: [PATCH 1/7] Add check_log_for_errors to detect and handle multiple errors --- easybuild/tools/run.py | 56 ++++++++++++++++++++++++++++++++++++++++++ test/framework/run.py | 56 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 1 deletion(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 381965aafe..086f53412c 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -589,3 +589,59 @@ def parse_log_for_error(txt, regExp=None, stdout=True, msg=None): (regExp, '\n'.join([x[0] for x in res]))) return res + + +def check_log_for_errors(logTxt, regExps): + """ + Check logTxt for messages matching regExps in order and do appropriate action + :param logTxt: String containing the log, will be split into individual lines + :param regExps: List of: regular expressions (as RE or string) to error on, + or tuple of regular expression and action (any of [IGNORE, WARN, ERROR]) + """ + global errors_found_in_log + + def is_regexp_object(objToTest): + try: + objToTest.match('') + return True + except AttributeError: + return False + + # Avoid accidentally passing a single element + assert isinstance(regExps, list), "regExps must be a list" + regExpTuples = [] + for cur in regExps: + try: + if isinstance(cur, str): + regExpTuples.append((re.compile(cur), ERROR)) + elif is_regexp_object(cur): + regExpTuples.append((cur, ERROR)) + elif len(cur) != 2: + raise TypeError("Invalid tuple") + elif not isinstance(cur[0], str) and not is_regexp_object(cur[0]): + raise TypeError("Invalid RegExp in tuple") + elif cur[1] not in (IGNORE, WARN, ERROR): + raise TypeError("Invalid action in tuple") + else: + regExpTuples.append((re.compile(cur[0]) if isinstance(cur[0], str) else cur[0], cur[1])) + except TypeError: + raise EasyBuildError("Invalid input: No RegExp or tuple of RegExp and action: %s" % str(cur)) + warnings = [] + errors = [] + for l in logTxt.split('\n'): + for regExp, action in regExpTuples: + m = regExp.search(l) + if m: + if action == ERROR: + errors.append(l) + elif action == WARN: + warnings.append(l) + break + errors_found_in_log += len(warnings) + len(errors) + if warnings: + _log.warning("Found %s potential errors in command output (output: %s)" % + (len(warnings), ", ".join(warnings))) + if errors: + raise EasyBuildError("Found %s errors in command output (output: %s)" % + (len(errors), ", ".join(errors))) + diff --git a/test/framework/run.py b/test/framework/run.py index 11398604f2..892408d3b1 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -46,7 +46,8 @@ import easybuild.tools.utilities from easybuild.tools.build_log import EasyBuildError, init_logging, stop_logging from easybuild.tools.filetools import adjust_permissions, read_file, write_file -from easybuild.tools.run import get_output_from_process, run_cmd, run_cmd_qa, parse_log_for_error +from easybuild.tools.run import get_output_from_process, run_cmd, run_cmd_qa, parse_log_for_error, check_log_for_errors +from easybuild.tools.config import ERROR, IGNORE, WARN class RunTest(EnhancedTestCase): @@ -520,6 +521,59 @@ def test_run_cmd_stream(self): ]) self.assertEqual(stdout, expected) + def test_check_log_for_errors(self): + fd, logfile = tempfile.mkstemp(suffix='.log', prefix='eb-test-') + os.close(fd) + + self.assertErrorRegex(EasyBuildError, "Invalid input:", check_log_for_errors, "", [42]) + self.assertErrorRegex(EasyBuildError, "Invalid input:", check_log_for_errors, "", [(42, IGNORE)]) + self.assertErrorRegex(EasyBuildError, "Invalid input:", check_log_for_errors, "", [("42", "invalid-mode")]) + self.assertErrorRegex(EasyBuildError, "Invalid input:", check_log_for_errors, "", [("42", IGNORE, "")]) + + input_text = "\n".join([ + "OK", + "error found", + "test failed", + "msg: allowed-test failed", + "enabling -Werror", + "the process crashed with 0" + ]) + expected_error_msg = r"Found 2 errors in command output \(output: error found, the process crashed with 0\)" + + self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + [r"\b(error|crashed)\b"]) + self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + [re.compile(r"\b(error|crashed)\b")]) + self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + [(r"\b(error|crashed)\b", ERROR)]) + self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + [(re.compile(r"\b(error|crashed)\b"), ERROR)]) + + expected_error_msg = "Found 2 potential errors in command output " \ + "(output: error found, the process crashed with 0)" + init_logging(logfile, silent=True) + check_log_for_errors(input_text, [(r"\b(error|crashed)\b", WARN)]) + stop_logging(logfile) + self.assertTrue(expected_error_msg in read_file(logfile)) + write_file(logfile, '') + init_logging(logfile, silent=True) + check_log_for_errors(input_text, [(re.compile(r"\b(error|crashed)\b"), WARN)]) + stop_logging(logfile) + self.assertTrue(expected_error_msg in read_file(logfile)) + + expected_error_msg = r"Found 2 errors in command output \(output: error found, test failed\)" + write_file(logfile, '') + init_logging(logfile, silent=True) + self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [ + r"\berror\b", + (r"\ballowed-test failed\b", IGNORE), + (re.compile(r"\bCRASHED\b", re.I), WARN), + "fail" + ]) + stop_logging(logfile) + expected_error_msg = "Found 1 potential errors in command output (output: the process crashed with 0)" + self.assertTrue(expected_error_msg in read_file(logfile)) + def suite(): """ returns all the testcases in this module """ From b98ac54523bb4ec20506b91f1ce022d889006cb3 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Wed, 11 Dec 2019 14:34:33 +0100 Subject: [PATCH 2/7] Adress review comments Rename variables Accept only strings not compiled regExps Split function in 2 for reuse --- easybuild/tools/run.py | 75 +++++++++++++++++++++--------------------- test/framework/run.py | 19 +++-------- 2 files changed, 42 insertions(+), 52 deletions(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 086f53412c..2b08a816b4 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -591,57 +591,56 @@ def parse_log_for_error(txt, regExp=None, stdout=True, msg=None): return res -def check_log_for_errors(logTxt, regExps): +def extract_errors_from_log(log_txt, reg_exps): """ - Check logTxt for messages matching regExps in order and do appropriate action - :param logTxt: String containing the log, will be split into individual lines - :param regExps: List of: regular expressions (as RE or string) to error on, + Check log_txt for messages matching regExps and return warnings and errors + :param log_txt: String containing the log, will be split into individual lines + :param reg_exps: List of: regular expressions (as strings) to error on, or tuple of regular expression and action (any of [IGNORE, WARN, ERROR]) + :return (warnings, errors) as lists of lines containing a match """ - global errors_found_in_log - - def is_regexp_object(objToTest): - try: - objToTest.match('') - return True - except AttributeError: - return False # Avoid accidentally passing a single element - assert isinstance(regExps, list), "regExps must be a list" - regExpTuples = [] - for cur in regExps: + assert isinstance(reg_exps, list), "reg_exps must be a list" + re_tuples = [] + for cur in reg_exps: try: if isinstance(cur, str): - regExpTuples.append((re.compile(cur), ERROR)) - elif is_regexp_object(cur): - regExpTuples.append((cur, ERROR)) - elif len(cur) != 2: - raise TypeError("Invalid tuple") - elif not isinstance(cur[0], str) and not is_regexp_object(cur[0]): - raise TypeError("Invalid RegExp in tuple") - elif cur[1] not in (IGNORE, WARN, ERROR): - raise TypeError("Invalid action in tuple") + reg_exp, action = cur, ERROR else: - regExpTuples.append((re.compile(cur[0]) if isinstance(cur[0], str) else cur[0], cur[1])) - except TypeError: - raise EasyBuildError("Invalid input: No RegExp or tuple of RegExp and action: %s" % str(cur)) + reg_exp, action = cur + if not isinstance(reg_exp, str) or action not in (IGNORE, WARN, ERROR): + raise TypeError("Invalid types") + re_tuples.append((re.compile(reg_exp), action)) + except Exception as e: + raise EasyBuildError("Invalid input: No RegExp or tuple of RegExp and action '%s' (%s)", str(cur), e) warnings = [] errors = [] - for l in logTxt.split('\n'): - for regExp, action in regExpTuples: - m = regExp.search(l) - if m: + for line in log_txt.split('\n'): + for reg_exp, action in re_tuples: + if reg_exp.search(line): if action == ERROR: - errors.append(l) + errors.append(line) elif action == WARN: - warnings.append(l) + warnings.append(line) break + return warnings, errors + + +def check_log_for_errors(log_txt, reg_exps): + """ + Check log_txt for messages matching regExps in order and do appropriate action + :param log_txt: String containing the log, will be split into individual lines + :param reg_exps: List of: regular expressions (as strings) to error on, + or tuple of regular expression and action (any of [IGNORE, WARN, ERROR]) + """ + global errors_found_in_log + warnings, errors = extract_errors_from_log(log_txt, reg_exps) + errors_found_in_log += len(warnings) + len(errors) if warnings: - _log.warning("Found %s potential errors in command output (output: %s)" % - (len(warnings), ", ".join(warnings))) + _log.warning("Found %s potential error(s) in command output (output: %s)", + len(warnings), ", ".join(warnings)) if errors: - raise EasyBuildError("Found %s errors in command output (output: %s)" % - (len(errors), ", ".join(errors))) - + raise EasyBuildError("Found %s error(s) in command output (output: %s)", + len(errors), ", ".join(errors)) diff --git a/test/framework/run.py b/test/framework/run.py index 892408d3b1..7b6957b4ef 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -538,40 +538,31 @@ def test_check_log_for_errors(self): "enabling -Werror", "the process crashed with 0" ]) - expected_error_msg = r"Found 2 errors in command output \(output: error found, the process crashed with 0\)" + expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found, the process crashed with 0\)" self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [r"\b(error|crashed)\b"]) - self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, - [re.compile(r"\b(error|crashed)\b")]) self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [(r"\b(error|crashed)\b", ERROR)]) - self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, - [(re.compile(r"\b(error|crashed)\b"), ERROR)]) - expected_error_msg = "Found 2 potential errors in command output " \ + expected_error_msg = "Found 2 potential error(s) in command output " \ "(output: error found, the process crashed with 0)" init_logging(logfile, silent=True) check_log_for_errors(input_text, [(r"\b(error|crashed)\b", WARN)]) stop_logging(logfile) self.assertTrue(expected_error_msg in read_file(logfile)) - write_file(logfile, '') - init_logging(logfile, silent=True) - check_log_for_errors(input_text, [(re.compile(r"\b(error|crashed)\b"), WARN)]) - stop_logging(logfile) - self.assertTrue(expected_error_msg in read_file(logfile)) - expected_error_msg = r"Found 2 errors in command output \(output: error found, test failed\)" + expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found, test failed\)" write_file(logfile, '') init_logging(logfile, silent=True) self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [ r"\berror\b", (r"\ballowed-test failed\b", IGNORE), - (re.compile(r"\bCRASHED\b", re.I), WARN), + (r"(?i)\bCRASHED\b", WARN), "fail" ]) stop_logging(logfile) - expected_error_msg = "Found 1 potential errors in command output (output: the process crashed with 0)" + expected_error_msg = "Found 1 potential error(s) in command output (output: the process crashed with 0)" self.assertTrue(expected_error_msg in read_file(logfile)) From ff159ea2e436d8da0a910a502cb68bb92e6c1d27 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 6 Jan 2020 10:11:38 +0100 Subject: [PATCH 3/7] Promote single string to list --- easybuild/tools/run.py | 7 +++++-- test/framework/run.py | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 2b08a816b4..5c8ea09429 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -600,8 +600,10 @@ def extract_errors_from_log(log_txt, reg_exps): :return (warnings, errors) as lists of lines containing a match """ - # Avoid accidentally passing a single element - assert isinstance(reg_exps, list), "reg_exps must be a list" + # promote single string value to list, since code below expects a list + if isinstance(reg_exps, string_type): + reg_exps = [reg_exps] + re_tuples = [] for cur in reg_exps: try: @@ -614,6 +616,7 @@ def extract_errors_from_log(log_txt, reg_exps): re_tuples.append((re.compile(reg_exp), action)) except Exception as e: raise EasyBuildError("Invalid input: No RegExp or tuple of RegExp and action '%s' (%s)", str(cur), e) + warnings = [] errors = [] for line in log_txt.split('\n'): diff --git a/test/framework/run.py b/test/framework/run.py index 7b6957b4ef..4f19b90273 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -540,8 +540,13 @@ def test_check_log_for_errors(self): ]) expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found, the process crashed with 0\)" + # String promoted to list + self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + r"\b(error|crashed)\b") + # List of string(s) self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [r"\b(error|crashed)\b"]) + # List of tuple(s) self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [(r"\b(error|crashed)\b", ERROR)]) From ebb76d024f9d242a3e06f6b1c39031e17130fbb3 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 6 Jan 2020 10:17:41 +0100 Subject: [PATCH 4/7] Separate errors by newline and ident next line --- easybuild/tools/run.py | 6 +++--- test/framework/run.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 5c8ea09429..da6960f92b 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -539,7 +539,7 @@ def parse_cmd_output(cmd, stdouterr, ec, simple, log_all, log_ok, regexp): if use_regexp or regexp: res = parse_log_for_error(stdouterr, regexp, msg="Command used: %s" % cmd) if len(res) > 0: - message = "Found %s errors in command output (output: %s)" % (len(res), ", ".join([r[0] for r in res])) + message = "Found %s errors in command output (output: %s)" % (len(res), "\n\t".join([r[0] for r in res])) if use_regexp: raise EasyBuildError(message) else: @@ -643,7 +643,7 @@ def check_log_for_errors(log_txt, reg_exps): errors_found_in_log += len(warnings) + len(errors) if warnings: _log.warning("Found %s potential error(s) in command output (output: %s)", - len(warnings), ", ".join(warnings)) + len(warnings), "\n\t".join(warnings)) if errors: raise EasyBuildError("Found %s error(s) in command output (output: %s)", - len(errors), ", ".join(errors)) + len(errors), "\n\t".join(errors)) diff --git a/test/framework/run.py b/test/framework/run.py index 4f19b90273..89c4525d95 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -538,7 +538,7 @@ def test_check_log_for_errors(self): "enabling -Werror", "the process crashed with 0" ]) - expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found, the process crashed with 0\)" + expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found\n\tthe process crashed with 0\)" # String promoted to list self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, @@ -551,13 +551,13 @@ def test_check_log_for_errors(self): [(r"\b(error|crashed)\b", ERROR)]) expected_error_msg = "Found 2 potential error(s) in command output " \ - "(output: error found, the process crashed with 0)" + "(output: error found\n\tthe process crashed with 0)" init_logging(logfile, silent=True) check_log_for_errors(input_text, [(r"\b(error|crashed)\b", WARN)]) stop_logging(logfile) self.assertTrue(expected_error_msg in read_file(logfile)) - expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found, test failed\)" + expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found\n\ttest failed\)" write_file(logfile, '') init_logging(logfile, silent=True) self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [ From f1e6f0ef9b0ce5492a2c4e7445446d6bd3cf92be Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Mon, 6 Jan 2020 10:25:53 +0100 Subject: [PATCH 5/7] Fix formatting --- test/framework/run.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/test/framework/run.py b/test/framework/run.py index 89c4525d95..a52c9b340c 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -46,7 +46,13 @@ import easybuild.tools.utilities from easybuild.tools.build_log import EasyBuildError, init_logging, stop_logging from easybuild.tools.filetools import adjust_permissions, read_file, write_file -from easybuild.tools.run import get_output_from_process, run_cmd, run_cmd_qa, parse_log_for_error, check_log_for_errors +from easybuild.tools.run import ( + check_log_for_errors, + get_output_from_process, + run_cmd, + run_cmd_qa, + parse_log_for_error, +) from easybuild.tools.config import ERROR, IGNORE, WARN @@ -538,7 +544,8 @@ def test_check_log_for_errors(self): "enabling -Werror", "the process crashed with 0" ]) - expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found\n\tthe process crashed with 0\)" + expected_error_msg = r"Found 2 error\(s\) in command output "\ + r"\(output: error found\n\tthe process crashed with 0\)" # String promoted to list self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, From c5ca2d92cd455180e94b35d61d19117992e75e65 Mon Sep 17 00:00:00 2001 From: Alexander Grund Date: Tue, 14 Jan 2020 14:13:36 +0100 Subject: [PATCH 6/7] Enhance TypeError and rename variable in test --- easybuild/tools/run.py | 7 +++++-- test/framework/run.py | 24 ++++++++++++------------ 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index da6960f92b..356c169976 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -599,6 +599,7 @@ def extract_errors_from_log(log_txt, reg_exps): or tuple of regular expression and action (any of [IGNORE, WARN, ERROR]) :return (warnings, errors) as lists of lines containing a match """ + actions = (IGNORE, WARN, ERROR) # promote single string value to list, since code below expects a list if isinstance(reg_exps, string_type): @@ -611,8 +612,10 @@ def extract_errors_from_log(log_txt, reg_exps): reg_exp, action = cur, ERROR else: reg_exp, action = cur - if not isinstance(reg_exp, str) or action not in (IGNORE, WARN, ERROR): - raise TypeError("Invalid types") + if not isinstance(reg_exp, str): + raise TypeError("RegExp must be passed as string") + if action not in actions: + raise TypeError("action must be one of %s" % action) re_tuples.append((re.compile(reg_exp), action)) except Exception as e: raise EasyBuildError("Invalid input: No RegExp or tuple of RegExp and action '%s' (%s)", str(cur), e) diff --git a/test/framework/run.py b/test/framework/run.py index a52c9b340c..a5f1000e05 100644 --- a/test/framework/run.py +++ b/test/framework/run.py @@ -544,38 +544,38 @@ def test_check_log_for_errors(self): "enabling -Werror", "the process crashed with 0" ]) - expected_error_msg = r"Found 2 error\(s\) in command output "\ - r"\(output: error found\n\tthe process crashed with 0\)" + expected_msg = r"Found 2 error\(s\) in command output "\ + r"\(output: error found\n\tthe process crashed with 0\)" # String promoted to list - self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + self.assertErrorRegex(EasyBuildError, expected_msg, check_log_for_errors, input_text, r"\b(error|crashed)\b") # List of string(s) - self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + self.assertErrorRegex(EasyBuildError, expected_msg, check_log_for_errors, input_text, [r"\b(error|crashed)\b"]) # List of tuple(s) - self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, + self.assertErrorRegex(EasyBuildError, expected_msg, check_log_for_errors, input_text, [(r"\b(error|crashed)\b", ERROR)]) - expected_error_msg = "Found 2 potential error(s) in command output " \ - "(output: error found\n\tthe process crashed with 0)" + expected_msg = "Found 2 potential error(s) in command output " \ + "(output: error found\n\tthe process crashed with 0)" init_logging(logfile, silent=True) check_log_for_errors(input_text, [(r"\b(error|crashed)\b", WARN)]) stop_logging(logfile) - self.assertTrue(expected_error_msg in read_file(logfile)) + self.assertTrue(expected_msg in read_file(logfile)) - expected_error_msg = r"Found 2 error\(s\) in command output \(output: error found\n\ttest failed\)" + expected_msg = r"Found 2 error\(s\) in command output \(output: error found\n\ttest failed\)" write_file(logfile, '') init_logging(logfile, silent=True) - self.assertErrorRegex(EasyBuildError, expected_error_msg, check_log_for_errors, input_text, [ + self.assertErrorRegex(EasyBuildError, expected_msg, check_log_for_errors, input_text, [ r"\berror\b", (r"\ballowed-test failed\b", IGNORE), (r"(?i)\bCRASHED\b", WARN), "fail" ]) stop_logging(logfile) - expected_error_msg = "Found 1 potential error(s) in command output (output: the process crashed with 0)" - self.assertTrue(expected_error_msg in read_file(logfile)) + expected_msg = "Found 1 potential error(s) in command output (output: the process crashed with 0)" + self.assertTrue(expected_msg in read_file(logfile)) def suite(): From 0859617731c4370304a04f4afcc1c6ce62e35b97 Mon Sep 17 00:00:00 2001 From: Kenneth Hoste Date: Tue, 14 Jan 2020 19:23:11 +0100 Subject: [PATCH 7/7] improve error messages in extract_errors_from_log --- easybuild/tools/run.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/easybuild/tools/run.py b/easybuild/tools/run.py index 356c169976..7e22e8c0ad 100644 --- a/easybuild/tools/run.py +++ b/easybuild/tools/run.py @@ -593,7 +593,8 @@ def parse_log_for_error(txt, regExp=None, stdout=True, msg=None): def extract_errors_from_log(log_txt, reg_exps): """ - Check log_txt for messages matching regExps and return warnings and errors + Check provided string (command output) for messages matching specified regular expressions, + and return 2-tuple with list of warnings and errors. :param log_txt: String containing the log, will be split into individual lines :param reg_exps: List of: regular expressions (as strings) to error on, or tuple of regular expression and action (any of [IGNORE, WARN, ERROR]) @@ -609,16 +610,21 @@ def extract_errors_from_log(log_txt, reg_exps): for cur in reg_exps: try: if isinstance(cur, str): + # use ERROR as default action if only regexp pattern is specified reg_exp, action = cur, ERROR - else: + elif isinstance(cur, tuple) and len(cur) == 2: reg_exp, action = cur + else: + raise TypeError("Incorrect type of value, expected string or 2-tuple") + if not isinstance(reg_exp, str): - raise TypeError("RegExp must be passed as string") + raise TypeError("Regular expressions must be passed as string, got %s" % type(reg_exp)) if action not in actions: - raise TypeError("action must be one of %s" % action) + raise TypeError("action must be one of %s, got %s" % (actions, action)) + re_tuples.append((re.compile(reg_exp), action)) - except Exception as e: - raise EasyBuildError("Invalid input: No RegExp or tuple of RegExp and action '%s' (%s)", str(cur), e) + except Exception as err: + raise EasyBuildError("Invalid input: No regexp or tuple of regexp and action '%s': %s", str(cur), err) warnings = [] errors = []