5Core methods to extract options information from ConfigBlock classes and merge with
6output variables metadata from a YAML file.
12from typing
import Any, Dict, List, Type, Optional, Union
14from AthenaCommon.Utils.unixtools
import find_datafile
19 Load output variables metadata from YAML file.
24 description: Variable description
25 toggled_by: Optional condition description
28 yaml_filepath: Path to the YAML file
31 Dictionary mapping block class names to their output variables
33 with open(yaml_filepath,
"r")
as f:
34 data = yaml.safe_load(f)
35 return data
if data
else {}
40 Extract options information from a ConfigBlock subclass.
43 block_class: A class that inherits from ConfigBlock
46 A dictionary containing the class name and its options
49 instance = block_class()
52 options_dict = instance.getOptions()
56 for option_name, option_obj
in options_dict.items():
58 if option_name
in [
"groupName",
"propertyOverrides",
"ignoreDependencies"]:
62 "type": option_obj.type.__name__
if option_obj.type
is not None else "None",
63 "default": option_obj.default,
64 "info": option_obj.info,
65 "required": option_obj.required,
66 "noneAction": option_obj.noneAction,
71 hasattr(instance,
"_expertModeSettings")
72 and option_name
in instance._expertModeSettings
74 expert_rule = instance._expertModeSettings[option_name]
75 if not isinstance(expert_rule, list):
76 expert_rule = [expert_rule]
79 option_info[
"expertMode"] = expert_rule
80 options_list.append(option_info)
83 "class": block_class.__name__,
84 "module": block_class.__module__,
85 "docstring": inspect.getdoc(block_class),
86 "options": options_list,
92 Extract a physical unit from an info string.
93 Currently looks for energy units like MeV or GeV.
96 info: The information string from an option.
99 The detected unit as a string ("MeV", "GeV", etc.) or None if no unit is found.
119 for pattern
in patterns:
120 if re.search(pattern, info):
123 elif "GeV" in pattern:
125 elif "mm" in pattern:
132 Scan an info string for backtick-enclosed substrings of the form `A::B`.
133 Turn them into a link to the appropriate module/files.
136 info: The input info string.
139 The processed string with Markdown links where applicable.
145 pattern =
r"`([^`]+)::([^`]+)`"
147 def replace_match(match):
148 A, B = match.group(1), match.group(2)
149 if A ==
"CP" or A ==
"ORUtils":
150 url = f
"https://acode-browser1.usatlas.bnl.gov/lxr/search?%21v=head&_filestring=**{B}**&_string="
151 return f
"[`{A}::{B}`]({url})"
152 elif A ==
"xAOD" or A ==
"AthOnnx":
153 url = f
"https://acode-browser1.usatlas.bnl.gov/lxr/ident?v=head&_i={B}&_identdefonly=1&_remember=1"
158 return re.sub(pattern, replace_match, info)
163 Convert JIRA ticket references in a string to Markdown links.
165 - JIRA tickets are of the form: all-caps letters, a dash, then digits (e.g., ATLASG-2358)
166 - Converted to Markdown links: [ATLASG-2358](https://its.cern.ch/jira/browse/ATLASG-2358)
169 info: Input string that may contain JIRA tickets.
172 The string with JIRA tickets converted to Markdown links.
178 pattern =
r"\b([A-Z]+-\d+)\b"
180 def replace_match(match):
181 ticket = match.group(1)
182 url = f
"https://its.cern.ch/jira/browse/{ticket}"
183 return f
"[{ticket}]({url})"
185 return re.sub(pattern, replace_match, info)
189 block_classes: List[Type], output_vars_yaml: Optional[Union[str, List[str]]] =
None
190) -> List[Dict[str, Any]]:
192 Extract options information from a list of ConfigBlock classes and merge
193 with output variables metadata.
196 block_classes: List of classes that inherit from ConfigBlock
197 output_vars_yaml: Optional path to YAML file or list of paths to YAML files
198 containing output variables. If multiple files provided,
199 their contents will be merged. Files are located using
203 List of dictionaries, each containing information about a block class
211 if isinstance(output_vars_yaml, str)
212 else output_vars_yaml
216 for yaml_file
in yaml_files:
219 resolved_path = find_datafile(yaml_file)
220 if resolved_path
is None:
221 raise FileNotFoundError(f
"Could not locate YAML file: {yaml_file}")
225 for class_name, variables
in file_vars.items():
226 if class_name
in output_vars_map:
228 output_vars_map[class_name].extend(variables)
230 output_vars_map[class_name] = variables
233 for block_class
in block_classes:
236 class_name = block_class.__name__
237 info[
"output_variables"] = output_vars_map.get(class_name, [])
245 """Save extracted data as YAML."""
246 with open(filepath,
"w")
as f:
247 yaml.dump(data, f, default_flow_style=
False, sort_keys=
False)
248 print(f
"Saved YAML to {filepath}")
253 Generate Markdown documentation for a single block.
256 block_info: Dictionary containing block information with keys:
257 - class: Block class name
258 - module: Module containing the block
259 - options: List of option dictionaries
260 - output_variables: List of output variable dictionaries
263 Markdown string for this block
268 if block_info.get(
"options"):
269 for opt
in block_info[
"options"]:
273 if name
in [
"skipOnData",
"skipOnMC"]:
274 if not opt[
"default"]
is True:
277 if name
in [
"onlyForDSIDs"]:
278 if not opt[
"default"]
is []:
282 label = f
"`{opt['label']}` ({opt['type']})"
283 if opt[
"expertMode"]
is not None:
284 expertOptions = list(opt[
"expertMode"])
285 label += f
" **[expert-only options: {','.join(['`' + str(x) + '`' for x in expertOptions])}]**"
286 if opt[
"required"]
is True or opt[
"noneAction"] !=
"ignore":
287 label +=
" **[REQUIRED]**"
289 markdown += f
"{label}\n"
290 info_string = opt[
"info"]
293 markdown += f
": {info_string}"
295 if opt.get(
"default") !=
"":
296 default_val = opt[
"default"]
297 default_str = repr(default_val)
300 unit = opt.get(
"physicalUnit")
301 if unit
is None or default_val
is None:
302 default_display = f
"`{default_str}`"
304 default_display = f
"`{default_str}` GeV"
308 if isinstance(default_val, (list, tuple)):
309 converted = [float(x) / 1000
for x
in default_val]
311 "[" +
", ".join(f
"{x}" for x
in converted) +
"]"
314 converted = float(default_val) / 1000
315 converted_str = f
"{converted}"
316 except (TypeError, ValueError):
318 default_display = f
"`{default_str}` MeV (`{converted_str}` GeV)"
320 default_display = f
"`{default_str}` {unit}"
322 markdown += f
" Default: {default_display}."
327 if block_info.get(
"output_variables"):
332 for var
in block_info[
"output_variables"]:
333 if var.get(
"toggled_by"):
334 condition = var[
"toggled_by"]
335 if condition
not in toggled_vars:
336 toggled_vars[condition] = []
337 toggled_vars[condition].append(var)
339 always_saved.append(var)
343 markdown +=
'!!! success "Registers the following variables:"\n'
344 for var
in always_saved:
345 var_name = var.get(
"name",
"N/A")
346 var_desc = var.get(
"description",
"")
347 markdown += f
" - `{var_name}`: {var_desc}\n"
351 for condition, vars_list
in toggled_vars.items():
353 f
'!!! success "Additional variables toggled by `{condition}`:"\n'
355 for var
in vars_list:
356 var_name = var.get(
"name",
"N/A")
357 var_desc = var.get(
"description",
"")
358 markdown += f
" - `{var_name}`: {var_desc}\n"
362 f
" Block {block_info.get('class')} didn't register any output variables."
369 input_filepath: str, output_filepath: str, block_data: List[Dict[str, Any]]
372 Process an input markdown file, replacing AUTOGEN<BlockName> markers
373 with generated block documentation.
375 Looks for lines of the form "AUTOGEN<BlockName>" and replaces them
376 with the generated markdown for that block. All other content is
380 input_filepath: Path to the input markdown file
381 output_filepath: Path to write the processed output
382 block_data: List of extracted block information dictionaries
385 FileNotFoundError: If input file does not exist
386 ValueError: If a referenced block is not found in block_data
389 block_markdown_map = {
394 with open(input_filepath,
"r")
as f:
395 lines = f.readlines()
399 stripped = line.strip()
402 if stripped.startswith(
"AUTOGEN<")
and stripped.endswith(
">"):
404 block_name = stripped[8:-1]
406 if block_name
in block_markdown_map:
407 output_lines.append(block_markdown_map[block_name])
410 f
"Block '{block_name}' not found in extracted block data"
413 output_lines.append(line)
416 with open(output_filepath,
"w")
as f:
417 f.writelines(output_lines)
419 print(f
"Processed markdown saved to {output_filepath}")
void print(char *figname, TCanvas *c1)
Dict[str, Any] extract_block_options(Type block_class)
Optional[str] interpret_physical_unit(str info)
str process_info_links(str info)
List[Dict[str, Any]] extract_from_classes(List[Type] block_classes, Optional[Union[str, List[str]]] output_vars_yaml=None)
None process_markdown_with_autogen(str input_filepath, str output_filepath, List[Dict[str, Any]] block_data)
Dict[str, List[Dict[str, Any]]] load_output_variables(str yaml_filepath)
str link_jira_tickets(str info)
None save_as_yaml(List[Dict[str, Any]] data, str filepath)
str generate_block_markdown(Dict[str, Any] block_info)