22import time
33from concurrent .futures import ThreadPoolExecutor , TimeoutError , as_completed
44from dataclasses import dataclass , field
5+ from functools import partial
56from typing import Callable , Dict , List , Optional , Tuple
67
78import click
@@ -74,6 +75,13 @@ def estimated_monthly_cost(self) -> float:
7475GCP_AI_RULES : List [Callable ] = list (GCP_RULE_MAP_AI .values ())
7576
7677
78+ def _rule_label (rule : Callable ) -> str :
79+ current = rule
80+ while isinstance (current , partial ):
81+ current = current .func
82+ return getattr (current , "RULE_ID" , getattr (current , "__name__" , repr (current )))
83+
84+
7785def _run_rule_with_retry (
7886 rule : Callable ,
7987 project_id : str ,
@@ -92,7 +100,7 @@ def _run_rule_with_retry(
92100 if attempt < _MAX_RETRIES - 1 :
93101 wait = (2 ** attempt ) + random .uniform (0 , 1 )
94102 click .echo (
95- f" Retrying { rule . __name__ } "
103+ f" Retrying { _rule_label ( rule ) } "
96104 f"(attempt { attempt + 2 } /{ _MAX_RETRIES } , wait { wait } s) ..."
97105 )
98106 time .sleep (min (wait , 60 ))
@@ -269,7 +277,7 @@ def _scan_gcp_project(
269277
270278 for future in as_completed (futures ):
271279 rule = futures [future ]
272- rule_id = getattr (rule , "RULE_ID" , rule . __name__ )
280+ rule_id = _rule_label (rule )
273281 try :
274282 rule_findings = future .result (timeout = 120 )
275283 for f in rule_findings :
0 commit comments