3636import pkgutil
3737import sys
3838from pathlib import Path
39- from typing import Any , Iterable , List , Type
39+ from typing import Any , Iterable , List , Optional , Type
4040
4141LINK_BASE_DEFAULT = "https://github.com/amd/node-scraper/blob/HEAD/"
4242REL_ROOT_DEFAULT = "nodescraper/plugins/inband"
@@ -50,7 +50,7 @@ def get_attr(obj: Any, name: str, default: Any = None) -> Any:
5050 return default
5151
5252
53- def _slice_from_rel_root (p : Path , rel_root : str | None ) -> str | None :
53+ def _slice_from_rel_root (p : Path , rel_root : Optional [ str ] ) -> Optional [ str ] :
5454 if not rel_root :
5555 return None
5656 parts = list (p .parts )
@@ -63,7 +63,7 @@ def _slice_from_rel_root(p: Path, rel_root: str | None) -> str | None:
6363 return None
6464
6565
66- def setup_link (class_data , link_base : str , rel_root : str | None ) -> str :
66+ def setup_link (class_data , link_base : str , rel_root : Optional [ str ] ) -> str :
6767 try :
6868 file_location = Path (inspect .getfile (class_data )).resolve ()
6969 except Exception :
@@ -80,7 +80,7 @@ def setup_link(class_data, link_base: str, rel_root: str | None) -> str:
8080 return base + rel_path
8181
8282
83- def get_own_doc (cls : type ) -> str | None :
83+ def get_own_doc (cls : type ) -> Optional [ str ] :
8484 """
8585 Return only the __doc__ defined in the class itself, ignore inheritance.
8686 """
@@ -224,6 +224,57 @@ def add_cmd(s: Any):
224224 return cmds
225225
226226
227+ def extract_regexes_and_args_from_analyzer (
228+ analyzer_cls : type , args_cls : Optional [type ]
229+ ) -> List [str ]:
230+ """Extract regex patterns and analyzer args from analyzer class"""
231+ if not inspect .isclass (analyzer_cls ):
232+ return []
233+
234+ output : List [str ] = []
235+
236+ # Check for ERROR_REGEX class variable (used by RegexAnalyzer subclasses like DmesgAnalyzer)
237+ error_regex = get_attr (analyzer_cls , "ERROR_REGEX" , None )
238+ if error_regex and isinstance (error_regex , list ):
239+ output .append ("**Built-in Regexes:**" )
240+ for item in error_regex :
241+ # ErrorRegex objects have regex, message, event_category attributes
242+ if hasattr (item , "regex" ):
243+ pattern = getattr (item .regex , "pattern" , None )
244+ message = getattr (item , "message" , "" )
245+ if pattern :
246+ # Truncate long patterns
247+ pattern_str = pattern if len (pattern ) < 50 else pattern [:47 ] + "..."
248+ output .append (f"- { message } : `{ pattern_str } `" )
249+ elif hasattr (item , "pattern" ):
250+ pattern_str = item .pattern if len (item .pattern ) < 50 else item .pattern [:47 ] + "..."
251+ output .append (f"- `{ pattern_str } `" )
252+
253+ # Check for other regex-related attributes
254+ for attr in dir (analyzer_cls ):
255+ if "REGEX" in attr .upper () and not attr .startswith ("_" ):
256+ val = get_attr (analyzer_cls , attr , default = None )
257+ if val is None or attr == "ERROR_REGEX" :
258+ continue
259+
260+ if hasattr (val , "pattern" ):
261+ output .append (f"**{ attr } **: `{ val .pattern } `" )
262+ elif isinstance (val , str ):
263+ output .append (f"**{ attr } **: `{ val } `" )
264+
265+ # Extract analyzer args if provided
266+ if inspect .isclass (args_cls ):
267+ anns = get_attr (args_cls , "__annotations__" , {}) or {}
268+ if anns :
269+ output .append ("**Analyzer Args:**" )
270+ for key , value in anns .items ():
271+ # Format the type annotation
272+ type_str = str (value ).replace ("typing." , "" )
273+ output .append (f"- `{ key } `: { type_str } " )
274+
275+ return output
276+
277+
227278def md_header (text : str , level : int = 2 ) -> str :
228279 return f"{ '#' * level } { text } \n \n "
229280
@@ -257,7 +308,20 @@ def class_vars_dump(cls: type, exclude: set) -> List[str]:
257308 continue
258309 if callable (val ) or isinstance (val , (staticmethod , classmethod , property )):
259310 continue
260- out .append (f"**{ name } **: `{ val } `" )
311+
312+ # Format list values with each item on a new line
313+ if isinstance (val , list ) and len (val ) > 0 :
314+ val_str = str (val )
315+ if len (val_str ) > 200 :
316+ formatted_items = []
317+ for item in val :
318+ formatted_items .append (f" { item } " )
319+ formatted_list = "[\n " + ",\n " .join (formatted_items ) + "\n ]"
320+ out .append (f"**{ name } **: `{ formatted_list } `" )
321+ else :
322+ out .append (f"**{ name } **: `{ val } `" )
323+ else :
324+ out .append (f"**{ name } **: `{ val } `" )
261325 return out
262326
263327
@@ -279,14 +343,20 @@ def generate_plugin_table_rows(plugins: List[type]) -> List[List[str]]:
279343 seen .add (key )
280344 uniq .append (c )
281345 cmds = uniq
346+
347+ # Extract regexes and args from analyzer
348+ regex_and_args = []
349+ if inspect .isclass (an ):
350+ regex_and_args = extract_regexes_and_args_from_analyzer (an , args )
351+
282352 rows .append (
283353 [
284- f"{ p .__module__ } .{ p .__name__ } " ,
354+ p .__name__ ,
355+ "<br>" .join (cmds ).replace ("|" , "\\ |" ) if cmds else "-" ,
356+ "<br>" .join (regex_and_args ).replace ("|" , "\\ |" ) if regex_and_args else "-" ,
285357 link_anchor (dm , "model" ) if inspect .isclass (dm ) else "-" ,
286358 link_anchor (col , "collector" ) if inspect .isclass (col ) else "-" ,
287359 link_anchor (an , "analyzer" ) if inspect .isclass (an ) else "-" ,
288- link_anchor (args , "args" ) if inspect .isclass (args ) else "-" ,
289- "<br>" .join (cmds ) if cmds else "-" ,
290360 ]
291361 )
292362 return rows
@@ -302,7 +372,7 @@ def render_table(headers: List[str], rows: List[List[str]]) -> str:
302372 return "" .join (out )
303373
304374
305- def render_collector_section (col : type , link_base : str , rel_root : str | None ) -> str :
375+ def render_collector_section (col : type , link_base : str , rel_root : Optional [ str ] ) -> str :
306376 hdr = md_header (f"Collector Class { col .__name__ } " , 2 )
307377 desc = sanitize_doc (get_own_doc (col ) or "" )
308378 s = hdr
@@ -335,7 +405,7 @@ def render_collector_section(col: type, link_base: str, rel_root: str | None) ->
335405 return s
336406
337407
338- def render_analyzer_section (an : type , link_base : str , rel_root : str | None ) -> str :
408+ def render_analyzer_section (an : type , link_base : str , rel_root : Optional [ str ] ) -> str :
339409 hdr = md_header (f"Data Analyzer Class { an .__name__ } " , 2 )
340410 desc = sanitize_doc (get_own_doc (an ) or "" )
341411 s = hdr
@@ -350,10 +420,18 @@ def render_analyzer_section(an: type, link_base: str, rel_root: str | None) -> s
350420 if cv :
351421 s += md_header ("Class Variables" , 3 ) + md_list (cv )
352422
423+ # Add regex patterns if present (pass None for args_cls since we don't have context here)
424+ regex_info = extract_regexes_and_args_from_analyzer (an , None )
425+ if regex_info :
426+ s += md_header ("Regex Patterns" , 3 )
427+ if len (regex_info ) > 10 :
428+ s += f"*{ len (regex_info )} items defined*\n \n "
429+ s += md_list (regex_info )
430+
353431 return s
354432
355433
356- def render_model_section (model : type , link_base : str , rel_root : str | None ) -> str :
434+ def render_model_section (model : type , link_base : str , rel_root : Optional [ str ] ) -> str :
357435 hdr = md_header (f"{ model .__name__ } Model" , 2 )
358436 desc = sanitize_doc (get_own_doc (model ) or "" )
359437 s = hdr
@@ -368,7 +446,7 @@ def render_model_section(model: type, link_base: str, rel_root: str | None) -> s
368446 return s
369447
370448
371- def render_analyzer_args_section (args_cls : type , link_base : str , rel_root : str | None ) -> str :
449+ def render_analyzer_args_section (args_cls : type , link_base : str , rel_root : Optional [ str ] ) -> str :
372450 hdr = md_header (f"Analyzer Args Class { args_cls .__name__ } " , 2 )
373451 desc = sanitize_doc (get_own_doc (args_cls ) or "" )
374452 s = hdr
@@ -418,7 +496,14 @@ def all_subclasses(cls: Type) -> set[type]:
418496 plugins .sort (key = lambda c : f"{ c .__module__ } .{ c .__name__ } " .lower ())
419497
420498 rows = generate_plugin_table_rows (plugins )
421- headers = ["Plugin" , "DataModel" , "Collector" , "Analyzer" , "AnalyzerArgs" , "Cmd(s)" ]
499+ headers = [
500+ "Plugin" ,
501+ "Collection" ,
502+ "Analysis" ,
503+ "DataModel" ,
504+ "Collector" ,
505+ "Analyzer" ,
506+ ]
422507
423508 collectors , analyzers , models , args_classes = [], [], [], []
424509 seen_c , seen_a , seen_m , seen_args = set (), set (), set (), set ()
0 commit comments