-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmatcher_classes.py
More file actions
88 lines (72 loc) · 2.94 KB
/
Copy pathmatcher_classes.py
File metadata and controls
88 lines (72 loc) · 2.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
import re
import sys
from dataclasses import dataclass
from typing import Any, Optional
if sys.version_info >= (3, 8):
from typing import Literal, Protocol
else:
from typing_extensions import Literal, Protocol
# ======================================================================================
@dataclass
class MatchResult:
status: Literal["OK", "WRONG RESULT", "N/A"]
error: Optional[str]
value: Optional[float]
# ======================================================================================
class Matcher(Protocol):
def run(self, output: str, **kwargs: Any) -> MatchResult: ...
# ======================================================================================
class GenericMatcher(Matcher):
def __init__(
self, pattern: str, col: int, regex: bool = False, abs_value: bool = False
):
self.pattern = pattern
self.regex_mode = regex
self.abs_value = abs_value
if not regex:
for c in r"[]()|+*?":
pattern = pattern.replace(c, f"\\{c}")
self.regex = re.compile(pattern)
self.col = col
def run(self, output: str, **kwargs: Any) -> MatchResult:
tol, ref = kwargs["tol"], kwargs["ref"]
assert isinstance(tol, float) or isinstance(ref, int)
assert isinstance(ref, float) or isinstance(ref, int)
# grep result
for line in reversed(output.split("\n")):
match = self.regex.search(line)
if match:
if self.regex_mode and match.groups():
value_str = match.group(1)
else:
value_str = line.split()[self.col - 1]
break
else:
error = f"Result not found: '{self.pattern}'.\n"
return MatchResult("WRONG RESULT", error, value=None)
# parse result
try:
value = float(value_str.replace("D", "E"))
if self.abs_value:
value = abs(value)
except:
error = f"Could not parse result as float: '{value_str}'.\n"
return MatchResult("WRONG RESULT", error, value=None)
# compare result to reference
diff = value - ref
rel_error = abs(diff / ref if ref != 0.0 else diff)
if rel_error > tol:
error = f"Difference too large: {rel_error:.2e} > {tol}, value: {value}.\n"
return MatchResult("WRONG RESULT", error, value)
return MatchResult("OK", error=None, value=value) # passed
# ======================================================================================
class TextPresenceMatcher(Matcher):
def __init__(self, text: str):
self.text = text
def run(self, output: str, **kwargs: Any) -> MatchResult:
if self.text not in output:
return MatchResult(
"WRONG RESULT", f"Text not found: '{self.text}'.\n", value=None
)
return MatchResult("OK", error=None, value=None)
# EOF