| 43 | |
| 44 | |
| 45 | class StatsCollector: |
| 46 | |
| 47 | def __init__(self) -> None: |
| 48 | # Timing |
| 49 | self._t_start: Optional[float] = None |
| 50 | self._t_end: Optional[float] = None |
| 51 | |
| 52 | # File metrics |
| 53 | self.files_scanned: int = 0 |
| 54 | self.files_skipped: int = 0 |
| 55 | self.parse_errors: int = 0 |
| 56 | self.total_loc: int = 0 |
| 57 | |
| 58 | # Rule metadata |
| 59 | self.rules_count: int = 0 |
| 60 | # rule_id → "regex" | "ast" | "taint" |
| 61 | self._rule_detection: Dict[str, str] = {} |
| 62 | |
| 63 | # Issue counters |
| 64 | self.pre_filter_count: int = 0 # raw from Rust (post dedup) |
| 65 | self.severity_filtered: int = 0 # dropped by --severity threshold |
| 66 | self.baseline_ignored: int = 0 # dropped by baseline file |
| 67 | self.final_issues: List[Any] = [] |
| 68 | |
| 69 | # Per-engine breakdown |
| 70 | self.regex_findings: int = 0 |
| 71 | self.ast_findings: int = 0 |
| 72 | self.taint_findings: int = 0 |
| 73 | |
| 74 | # Resource usage (populated by background thread) |
| 75 | self.peak_memory_mb: Optional[float] = None |
| 76 | self.cpu_cores_logical: Optional[int] = None |
| 77 | self.avg_cpu_percent: Optional[float] = None |
| 78 | self._cpu_samples: List[float] = [] |
| 79 | |
| 80 | self._mon_thread: Optional[threading.Thread] = None |
| 81 | self._stop_evt = threading.Event() |
| 82 | self._psutil_ok: bool = False |
| 83 | |
| 84 | |
| 85 | def start(self) -> None: |
| 86 | """Begin timing and background resource monitoring.""" |
| 87 | self._t_start = time.perf_counter() |
| 88 | self._launch_monitor() |
| 89 | |
| 90 | def stop(self) -> None: |
| 91 | """Stop timing and resource monitoring.""" |
| 92 | self._t_end = time.perf_counter() |
| 93 | self._stop_evt.set() |
| 94 | if self._mon_thread: |
| 95 | self._mon_thread.join(timeout=2.0) |
| 96 | if self._cpu_samples: |
| 97 | self.avg_cpu_percent = sum(self._cpu_samples) / len(self._cpu_samples) |
| 98 | |
| 99 | |
| 100 | def record_files( |
| 101 | self, |
| 102 | python_files_data: List[Dict[str, Any]], |