|
| 1 | +############################################################################### |
| 2 | +# |
| 3 | +# MIT License |
| 4 | +# |
| 5 | +# Copyright (c) 2026 Advanced Micro Devices, Inc. |
| 6 | +# |
| 7 | +# Permission is hereby granted, free of charge, to any person obtaining a copy |
| 8 | +# of this software and associated documentation files (the "Software"), to deal |
| 9 | +# in the Software without restriction, including without limitation the rights |
| 10 | +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 11 | +# copies of the Software, and to permit persons to whom the Software is |
| 12 | +# furnished to do so, subject to the following conditions: |
| 13 | +# |
| 14 | +# The above copyright notice and this permission notice shall be included in all |
| 15 | +# copies or substantial portions of the Software. |
| 16 | +# |
| 17 | +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 18 | +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 19 | +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 20 | +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 21 | +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 22 | +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 23 | +# SOFTWARE. |
| 24 | +# |
| 25 | +############################################################################### |
| 26 | +import os |
| 27 | +from typing import Optional, Union |
| 28 | + |
| 29 | +from nodescraper.base.regexanalyzer import ErrorRegex, RegexAnalyzer, RegexEvent |
| 30 | +from nodescraper.enums import ExecutionStatus |
| 31 | +from nodescraper.models import TaskResult |
| 32 | + |
| 33 | +from .analyzer_args import RegexSearchAnalyzerArgs |
| 34 | +from .regex_search_data import RegexSearchData |
| 35 | + |
| 36 | + |
| 37 | +class RegexSearchAnalyzer(RegexAnalyzer[RegexSearchData, RegexSearchAnalyzerArgs]): |
| 38 | + """Run user-provided regexes against text loaded from --data (file or directory).""" |
| 39 | + |
| 40 | + DATA_MODEL = RegexSearchData |
| 41 | + |
| 42 | + ERROR_REGEX: list[ErrorRegex] = [] |
| 43 | + |
| 44 | + def _build_regex_event( |
| 45 | + self, regex_obj: ErrorRegex, match: Union[str, list[str]], source: str |
| 46 | + ) -> RegexEvent: |
| 47 | + """Augment the default event text with a file path when the origin is a concrete path. |
| 48 | +
|
| 49 | + Args: |
| 50 | + regex_obj: Metadata for the rule that produced the match. |
| 51 | + match: Substring or grouped capture text from the pattern. |
| 52 | + source: Origin label, or an absolute path when matching per file. |
| 53 | +
|
| 54 | + Returns: |
| 55 | + Match record with an extended description when a path-like source is present. |
| 56 | + """ |
| 57 | + event = super()._build_regex_event(regex_obj, match, source) |
| 58 | + if source and source != "regex_search": |
| 59 | + event.description = f"{regex_obj.message} [file: {source}]" |
| 60 | + return event |
| 61 | + |
| 62 | + def analyze_data( |
| 63 | + self, |
| 64 | + data: RegexSearchData, |
| 65 | + args: Optional[RegexSearchAnalyzerArgs] = None, |
| 66 | + ) -> TaskResult: |
| 67 | + """Scan loaded inputs with the given patterns, or mark the task not run if inputs are incomplete. |
| 68 | +
|
| 69 | + Args: |
| 70 | + data: Aggregated and per-file text loaded from the user data path. |
| 71 | + args: Optional pattern list and timing knobs; omitted or empty patterns skip work. |
| 72 | +
|
| 73 | + Returns: |
| 74 | + Work outcome with match events, or a not-run status when patterns are absent. |
| 75 | + """ |
| 76 | + if args is None or not args.error_regex: |
| 77 | + self.result.status = ExecutionStatus.NOT_RAN |
| 78 | + self.result.message = "Analysis args need to be provided for the analyzer to run" |
| 79 | + return self.result |
| 80 | + |
| 81 | + final_regex = self._convert_and_extend_error_regex(args.error_regex, []) |
| 82 | + |
| 83 | + if data.files: |
| 84 | + for rel_path in sorted(data.files.keys()): |
| 85 | + file_content = data.files[rel_path] |
| 86 | + abs_source = os.path.normpath(os.path.join(data.data_root, rel_path)) |
| 87 | + self.result.events += self.check_all_regexes( |
| 88 | + content=file_content, |
| 89 | + source=abs_source, |
| 90 | + error_regex=final_regex, |
| 91 | + num_timestamps=args.num_timestamps, |
| 92 | + interval_to_collapse_event=args.interval_to_collapse_event, |
| 93 | + ) |
| 94 | + else: |
| 95 | + self.result.events += self.check_all_regexes( |
| 96 | + content=data.content, |
| 97 | + source=data.data_root or "regex_search", |
| 98 | + error_regex=final_regex, |
| 99 | + num_timestamps=args.num_timestamps, |
| 100 | + interval_to_collapse_event=args.interval_to_collapse_event, |
| 101 | + ) |
| 102 | + return self.result |
0 commit comments