1f061c9f7SBreno Leitao#!/usr/bin/env python3 2f061c9f7SBreno Leitao# SPDX-License-Identifier: GPL-2.0 3f061c9f7SBreno Leitao# -*- coding: utf-8; mode: python -*- 4f061c9f7SBreno Leitao 5f061c9f7SBreno Leitao""" 6f061c9f7SBreno Leitao Script to auto generate the documentation for Netlink specifications. 7f061c9f7SBreno Leitao 8f061c9f7SBreno Leitao :copyright: Copyright (C) 2023 Breno Leitao <leitao@debian.org> 9f061c9f7SBreno Leitao :license: GPL Version 2, June 1991 see linux/COPYING for details. 10f061c9f7SBreno Leitao 11f061c9f7SBreno Leitao This script performs extensive parsing to the Linux kernel's netlink YAML 12f061c9f7SBreno Leitao spec files, in an effort to avoid needing to heavily mark up the original 13f061c9f7SBreno Leitao YAML file. 14f061c9f7SBreno Leitao 15f061c9f7SBreno Leitao This code is split in three big parts: 16f061c9f7SBreno Leitao 1) RST formatters: Use to convert a string to a RST output 17f061c9f7SBreno Leitao 2) Parser helpers: Functions to parse the YAML data structure 18f061c9f7SBreno Leitao 3) Main function and small helpers 19f061c9f7SBreno Leitao""" 20f061c9f7SBreno Leitao 21f061c9f7SBreno Leitaofrom typing import Any, Dict, List 22f061c9f7SBreno Leitaoimport os.path 23f061c9f7SBreno Leitaoimport sys 24f061c9f7SBreno Leitaoimport argparse 25f061c9f7SBreno Leitaoimport logging 26f061c9f7SBreno Leitaoimport yaml 27f061c9f7SBreno Leitao 28f061c9f7SBreno Leitao 29f061c9f7SBreno LeitaoSPACE_PER_LEVEL = 4 30f061c9f7SBreno Leitao 31f061c9f7SBreno Leitao 32f061c9f7SBreno Leitao# RST Formatters 33f061c9f7SBreno Leitao# ============== 34f061c9f7SBreno Leitaodef headroom(level: int) -> str: 35f061c9f7SBreno Leitao """Return space to format""" 36f061c9f7SBreno Leitao return " " * (level * SPACE_PER_LEVEL) 37f061c9f7SBreno Leitao 38f061c9f7SBreno Leitao 39f061c9f7SBreno Leitaodef bold(text: str) -> str: 40f061c9f7SBreno Leitao """Format bold text""" 41f061c9f7SBreno Leitao return f"**{text}**" 42f061c9f7SBreno Leitao 43f061c9f7SBreno Leitao 44f061c9f7SBreno Leitaodef inline(text: str) -> str: 45f061c9f7SBreno Leitao """Format inline text""" 46f061c9f7SBreno Leitao return f"``{text}``" 47f061c9f7SBreno Leitao 48f061c9f7SBreno Leitao 49f061c9f7SBreno Leitaodef sanitize(text: str) -> str: 50f061c9f7SBreno Leitao """Remove newlines and multiple spaces""" 51f061c9f7SBreno Leitao # This is useful for some fields that are spread across multiple lines 52f061c9f7SBreno Leitao return str(text).replace("\n", "").strip() 53f061c9f7SBreno Leitao 54f061c9f7SBreno Leitao 55f061c9f7SBreno Leitaodef rst_fields(key: str, value: str, level: int = 0) -> str: 56f061c9f7SBreno Leitao """Return a RST formatted field""" 57f061c9f7SBreno Leitao return headroom(level) + f":{key}: {value}" 58f061c9f7SBreno Leitao 59f061c9f7SBreno Leitao 60f061c9f7SBreno Leitaodef rst_definition(key: str, value: Any, level: int = 0) -> str: 61f061c9f7SBreno Leitao """Format a single rst definition""" 62f061c9f7SBreno Leitao return headroom(level) + key + "\n" + headroom(level + 1) + str(value) 63f061c9f7SBreno Leitao 64f061c9f7SBreno Leitao 65f061c9f7SBreno Leitaodef rst_paragraph(paragraph: str, level: int = 0) -> str: 66f061c9f7SBreno Leitao """Return a formatted paragraph""" 67f061c9f7SBreno Leitao return headroom(level) + paragraph 68f061c9f7SBreno Leitao 69f061c9f7SBreno Leitao 70f061c9f7SBreno Leitaodef rst_bullet(item: str, level: int = 0) -> str: 71f061c9f7SBreno Leitao """Return a formatted a bullet""" 72f061c9f7SBreno Leitao return headroom(level) + f"- {item}" 73f061c9f7SBreno Leitao 74f061c9f7SBreno Leitao 75f061c9f7SBreno Leitaodef rst_subsection(title: str) -> str: 76f061c9f7SBreno Leitao """Add a sub-section to the document""" 77f061c9f7SBreno Leitao return f"{title}\n" + "-" * len(title) 78f061c9f7SBreno Leitao 79f061c9f7SBreno Leitao 80f061c9f7SBreno Leitaodef rst_subsubsection(title: str) -> str: 81f061c9f7SBreno Leitao """Add a sub-sub-section to the document""" 82f061c9f7SBreno Leitao return f"{title}\n" + "~" * len(title) 83f061c9f7SBreno Leitao 84f061c9f7SBreno Leitao 85f061c9f7SBreno Leitaodef rst_section(title: str) -> str: 86f061c9f7SBreno Leitao """Add a section to the document""" 87f061c9f7SBreno Leitao return f"\n{title}\n" + "=" * len(title) 88f061c9f7SBreno Leitao 89f061c9f7SBreno Leitao 90f061c9f7SBreno Leitaodef rst_subtitle(title: str) -> str: 91f061c9f7SBreno Leitao """Add a subtitle to the document""" 92f061c9f7SBreno Leitao return "\n" + "-" * len(title) + f"\n{title}\n" + "-" * len(title) + "\n\n" 93f061c9f7SBreno Leitao 94f061c9f7SBreno Leitao 95f061c9f7SBreno Leitaodef rst_title(title: str) -> str: 96f061c9f7SBreno Leitao """Add a title to the document""" 97f061c9f7SBreno Leitao return "=" * len(title) + f"\n{title}\n" + "=" * len(title) + "\n\n" 98f061c9f7SBreno Leitao 99f061c9f7SBreno Leitao 100f061c9f7SBreno Leitaodef rst_list_inline(list_: List[str], level: int = 0) -> str: 101f061c9f7SBreno Leitao """Format a list using inlines""" 102f061c9f7SBreno Leitao return headroom(level) + "[" + ", ".join(inline(i) for i in list_) + "]" 103f061c9f7SBreno Leitao 104f061c9f7SBreno Leitao 105f061c9f7SBreno Leitaodef rst_header() -> str: 106f061c9f7SBreno Leitao """The headers for all the auto generated RST files""" 107f061c9f7SBreno Leitao lines = [] 108f061c9f7SBreno Leitao 109f061c9f7SBreno Leitao lines.append(rst_paragraph(".. SPDX-License-Identifier: GPL-2.0")) 110f061c9f7SBreno Leitao lines.append(rst_paragraph(".. NOTE: This document was auto-generated.\n\n")) 111f061c9f7SBreno Leitao 112f061c9f7SBreno Leitao return "\n".join(lines) 113f061c9f7SBreno Leitao 114f061c9f7SBreno Leitao 115f061c9f7SBreno Leitaodef rst_toctree(maxdepth: int = 2) -> str: 116f061c9f7SBreno Leitao """Generate a toctree RST primitive""" 117f061c9f7SBreno Leitao lines = [] 118f061c9f7SBreno Leitao 119f061c9f7SBreno Leitao lines.append(".. toctree::") 120f061c9f7SBreno Leitao lines.append(f" :maxdepth: {maxdepth}\n\n") 121f061c9f7SBreno Leitao 122f061c9f7SBreno Leitao return "\n".join(lines) 123f061c9f7SBreno Leitao 124f061c9f7SBreno Leitao 125e8c780a5SJakub Kicinskidef rst_label(title: str) -> str: 126e8c780a5SJakub Kicinski """Return a formatted label""" 127e8c780a5SJakub Kicinski return f".. _{title}:\n\n" 128e8c780a5SJakub Kicinski 129e8c780a5SJakub Kicinski 130f061c9f7SBreno Leitao# Parsers 131f061c9f7SBreno Leitao# ======= 132f061c9f7SBreno Leitao 133f061c9f7SBreno Leitao 134f061c9f7SBreno Leitaodef parse_mcast_group(mcast_group: List[Dict[str, Any]]) -> str: 135f061c9f7SBreno Leitao """Parse 'multicast' group list and return a formatted string""" 136f061c9f7SBreno Leitao lines = [] 137f061c9f7SBreno Leitao for group in mcast_group: 138f061c9f7SBreno Leitao lines.append(rst_bullet(group["name"])) 139f061c9f7SBreno Leitao 140f061c9f7SBreno Leitao return "\n".join(lines) 141f061c9f7SBreno Leitao 142f061c9f7SBreno Leitao 143f061c9f7SBreno Leitaodef parse_do(do_dict: Dict[str, Any], level: int = 0) -> str: 144f061c9f7SBreno Leitao """Parse 'do' section and return a formatted string""" 145f061c9f7SBreno Leitao lines = [] 146f061c9f7SBreno Leitao for key in do_dict.keys(): 147f061c9f7SBreno Leitao lines.append(rst_paragraph(bold(key), level + 1)) 148f061c9f7SBreno Leitao lines.append(parse_do_attributes(do_dict[key], level + 1) + "\n") 149f061c9f7SBreno Leitao 150f061c9f7SBreno Leitao return "\n".join(lines) 151f061c9f7SBreno Leitao 152f061c9f7SBreno Leitao 153f061c9f7SBreno Leitaodef parse_do_attributes(attrs: Dict[str, Any], level: int = 0) -> str: 154f061c9f7SBreno Leitao """Parse 'attributes' section""" 155f061c9f7SBreno Leitao if "attributes" not in attrs: 156f061c9f7SBreno Leitao return "" 157f061c9f7SBreno Leitao lines = [rst_fields("attributes", rst_list_inline(attrs["attributes"]), level + 1)] 158f061c9f7SBreno Leitao 159f061c9f7SBreno Leitao return "\n".join(lines) 160f061c9f7SBreno Leitao 161f061c9f7SBreno Leitao 162f061c9f7SBreno Leitaodef parse_operations(operations: List[Dict[str, Any]]) -> str: 163f061c9f7SBreno Leitao """Parse operations block""" 164f061c9f7SBreno Leitao preprocessed = ["name", "doc", "title", "do", "dump"] 165f061c9f7SBreno Leitao lines = [] 166f061c9f7SBreno Leitao 167f061c9f7SBreno Leitao for operation in operations: 168f061c9f7SBreno Leitao lines.append(rst_section(operation["name"])) 169f061c9f7SBreno Leitao lines.append(rst_paragraph(sanitize(operation["doc"])) + "\n") 170f061c9f7SBreno Leitao 171f061c9f7SBreno Leitao for key in operation.keys(): 172f061c9f7SBreno Leitao if key in preprocessed: 173f061c9f7SBreno Leitao # Skip the special fields 174f061c9f7SBreno Leitao continue 175f061c9f7SBreno Leitao lines.append(rst_fields(key, operation[key], 0)) 176f061c9f7SBreno Leitao 177f061c9f7SBreno Leitao if "do" in operation: 178f061c9f7SBreno Leitao lines.append(rst_paragraph(":do:", 0)) 179f061c9f7SBreno Leitao lines.append(parse_do(operation["do"], 0)) 180f061c9f7SBreno Leitao if "dump" in operation: 181f061c9f7SBreno Leitao lines.append(rst_paragraph(":dump:", 0)) 182f061c9f7SBreno Leitao lines.append(parse_do(operation["dump"], 0)) 183f061c9f7SBreno Leitao 184f061c9f7SBreno Leitao # New line after fields 185f061c9f7SBreno Leitao lines.append("\n") 186f061c9f7SBreno Leitao 187f061c9f7SBreno Leitao return "\n".join(lines) 188f061c9f7SBreno Leitao 189f061c9f7SBreno Leitao 190f061c9f7SBreno Leitaodef parse_entries(entries: List[Dict[str, Any]], level: int) -> str: 191f061c9f7SBreno Leitao """Parse a list of entries""" 192f061c9f7SBreno Leitao lines = [] 193f061c9f7SBreno Leitao for entry in entries: 194f061c9f7SBreno Leitao if isinstance(entry, dict): 195f061c9f7SBreno Leitao # entries could be a list or a dictionary 196f061c9f7SBreno Leitao lines.append( 197f061c9f7SBreno Leitao rst_fields(entry.get("name", ""), sanitize(entry.get("doc", "")), level) 198f061c9f7SBreno Leitao ) 199f061c9f7SBreno Leitao elif isinstance(entry, list): 200f061c9f7SBreno Leitao lines.append(rst_list_inline(entry, level)) 201f061c9f7SBreno Leitao else: 202f061c9f7SBreno Leitao lines.append(rst_bullet(inline(sanitize(entry)), level)) 203f061c9f7SBreno Leitao 204f061c9f7SBreno Leitao lines.append("\n") 205f061c9f7SBreno Leitao return "\n".join(lines) 206f061c9f7SBreno Leitao 207f061c9f7SBreno Leitao 208f061c9f7SBreno Leitaodef parse_definitions(defs: Dict[str, Any]) -> str: 209f061c9f7SBreno Leitao """Parse definitions section""" 210f061c9f7SBreno Leitao preprocessed = ["name", "entries", "members"] 211f061c9f7SBreno Leitao ignored = ["render-max"] # This is not printed 212f061c9f7SBreno Leitao lines = [] 213f061c9f7SBreno Leitao 214f061c9f7SBreno Leitao for definition in defs: 215f061c9f7SBreno Leitao lines.append(rst_section(definition["name"])) 216f061c9f7SBreno Leitao for k in definition.keys(): 217f061c9f7SBreno Leitao if k in preprocessed + ignored: 218f061c9f7SBreno Leitao continue 219f061c9f7SBreno Leitao lines.append(rst_fields(k, sanitize(definition[k]), 0)) 220f061c9f7SBreno Leitao 221f061c9f7SBreno Leitao # Field list needs to finish with a new line 222f061c9f7SBreno Leitao lines.append("\n") 223f061c9f7SBreno Leitao if "entries" in definition: 224f061c9f7SBreno Leitao lines.append(rst_paragraph(":entries:", 0)) 225f061c9f7SBreno Leitao lines.append(parse_entries(definition["entries"], 1)) 226f061c9f7SBreno Leitao if "members" in definition: 227f061c9f7SBreno Leitao lines.append(rst_paragraph(":members:", 0)) 228f061c9f7SBreno Leitao lines.append(parse_entries(definition["members"], 1)) 229f061c9f7SBreno Leitao 230f061c9f7SBreno Leitao return "\n".join(lines) 231f061c9f7SBreno Leitao 232f061c9f7SBreno Leitao 233f061c9f7SBreno Leitaodef parse_attr_sets(entries: List[Dict[str, Any]]) -> str: 234f061c9f7SBreno Leitao """Parse attribute from attribute-set""" 235f061c9f7SBreno Leitao preprocessed = ["name", "type"] 236f061c9f7SBreno Leitao ignored = ["checks"] 237f061c9f7SBreno Leitao lines = [] 238f061c9f7SBreno Leitao 239f061c9f7SBreno Leitao for entry in entries: 240f061c9f7SBreno Leitao lines.append(rst_section(entry["name"])) 241f061c9f7SBreno Leitao for attr in entry["attributes"]: 242f061c9f7SBreno Leitao type_ = attr.get("type") 243e9d7c592SDonald Hunter attr_line = attr["name"] 244f061c9f7SBreno Leitao if type_: 245f061c9f7SBreno Leitao # Add the attribute type in the same line 246f061c9f7SBreno Leitao attr_line += f" ({inline(type_)})" 247f061c9f7SBreno Leitao 248f061c9f7SBreno Leitao lines.append(rst_subsubsection(attr_line)) 249f061c9f7SBreno Leitao 250f061c9f7SBreno Leitao for k in attr.keys(): 251f061c9f7SBreno Leitao if k in preprocessed + ignored: 252f061c9f7SBreno Leitao continue 253*9b0aa224SDonald Hunter lines.append(rst_fields(k, sanitize(attr[k]), 0)) 254f061c9f7SBreno Leitao lines.append("\n") 255f061c9f7SBreno Leitao 256f061c9f7SBreno Leitao return "\n".join(lines) 257f061c9f7SBreno Leitao 258f061c9f7SBreno Leitao 2596235b3d8SDonald Hunterdef parse_sub_messages(entries: List[Dict[str, Any]]) -> str: 2606235b3d8SDonald Hunter """Parse sub-message definitions""" 2616235b3d8SDonald Hunter lines = [] 2626235b3d8SDonald Hunter 2636235b3d8SDonald Hunter for entry in entries: 2646235b3d8SDonald Hunter lines.append(rst_section(entry["name"])) 2656235b3d8SDonald Hunter for fmt in entry["formats"]: 2666235b3d8SDonald Hunter value = fmt["value"] 2676235b3d8SDonald Hunter 2686235b3d8SDonald Hunter lines.append(rst_bullet(bold(value))) 2696235b3d8SDonald Hunter for attr in ['fixed-header', 'attribute-set']: 2706235b3d8SDonald Hunter if attr in fmt: 271*9b0aa224SDonald Hunter lines.append(rst_fields(attr, fmt[attr], 1)) 2726235b3d8SDonald Hunter lines.append("\n") 2736235b3d8SDonald Hunter 2746235b3d8SDonald Hunter return "\n".join(lines) 2756235b3d8SDonald Hunter 2766235b3d8SDonald Hunter 277f061c9f7SBreno Leitaodef parse_yaml(obj: Dict[str, Any]) -> str: 278f061c9f7SBreno Leitao """Format the whole YAML into a RST string""" 279f061c9f7SBreno Leitao lines = [] 280f061c9f7SBreno Leitao 281f061c9f7SBreno Leitao # Main header 282f061c9f7SBreno Leitao 283f061c9f7SBreno Leitao lines.append(rst_header()) 284f061c9f7SBreno Leitao 285f061c9f7SBreno Leitao title = f"Family ``{obj['name']}`` netlink specification" 286f061c9f7SBreno Leitao lines.append(rst_title(title)) 287f061c9f7SBreno Leitao lines.append(rst_paragraph(".. contents::\n")) 288f061c9f7SBreno Leitao 289f061c9f7SBreno Leitao if "doc" in obj: 290f061c9f7SBreno Leitao lines.append(rst_subtitle("Summary")) 291f061c9f7SBreno Leitao lines.append(rst_paragraph(obj["doc"], 0)) 292f061c9f7SBreno Leitao 293f061c9f7SBreno Leitao # Operations 294f061c9f7SBreno Leitao if "operations" in obj: 295f061c9f7SBreno Leitao lines.append(rst_subtitle("Operations")) 296f061c9f7SBreno Leitao lines.append(parse_operations(obj["operations"]["list"])) 297f061c9f7SBreno Leitao 298f061c9f7SBreno Leitao # Multicast groups 299f061c9f7SBreno Leitao if "mcast-groups" in obj: 300f061c9f7SBreno Leitao lines.append(rst_subtitle("Multicast groups")) 301f061c9f7SBreno Leitao lines.append(parse_mcast_group(obj["mcast-groups"]["list"])) 302f061c9f7SBreno Leitao 303f061c9f7SBreno Leitao # Definitions 304f061c9f7SBreno Leitao if "definitions" in obj: 305f061c9f7SBreno Leitao lines.append(rst_subtitle("Definitions")) 306f061c9f7SBreno Leitao lines.append(parse_definitions(obj["definitions"])) 307f061c9f7SBreno Leitao 308f061c9f7SBreno Leitao # Attributes set 309f061c9f7SBreno Leitao if "attribute-sets" in obj: 310f061c9f7SBreno Leitao lines.append(rst_subtitle("Attribute sets")) 311f061c9f7SBreno Leitao lines.append(parse_attr_sets(obj["attribute-sets"])) 312f061c9f7SBreno Leitao 3136235b3d8SDonald Hunter # Sub-messages 3146235b3d8SDonald Hunter if "sub-messages" in obj: 3156235b3d8SDonald Hunter lines.append(rst_subtitle("Sub-messages")) 3166235b3d8SDonald Hunter lines.append(parse_sub_messages(obj["sub-messages"])) 3176235b3d8SDonald Hunter 318f061c9f7SBreno Leitao return "\n".join(lines) 319f061c9f7SBreno Leitao 320f061c9f7SBreno Leitao 321f061c9f7SBreno Leitao# Main functions 322f061c9f7SBreno Leitao# ============== 323f061c9f7SBreno Leitao 324f061c9f7SBreno Leitao 325f061c9f7SBreno Leitaodef parse_arguments() -> argparse.Namespace: 326f061c9f7SBreno Leitao """Parse arguments from user""" 327f061c9f7SBreno Leitao parser = argparse.ArgumentParser(description="Netlink RST generator") 328f061c9f7SBreno Leitao 329f061c9f7SBreno Leitao parser.add_argument("-v", "--verbose", action="store_true") 330f061c9f7SBreno Leitao parser.add_argument("-o", "--output", help="Output file name") 331f061c9f7SBreno Leitao 332f061c9f7SBreno Leitao # Index and input are mutually exclusive 333f061c9f7SBreno Leitao group = parser.add_mutually_exclusive_group() 334f061c9f7SBreno Leitao group.add_argument( 335f061c9f7SBreno Leitao "-x", "--index", action="store_true", help="Generate the index page" 336f061c9f7SBreno Leitao ) 337f061c9f7SBreno Leitao group.add_argument("-i", "--input", help="YAML file name") 338f061c9f7SBreno Leitao 339f061c9f7SBreno Leitao args = parser.parse_args() 340f061c9f7SBreno Leitao 341f061c9f7SBreno Leitao if args.verbose: 342f061c9f7SBreno Leitao logging.basicConfig(level=logging.DEBUG) 343f061c9f7SBreno Leitao 344f061c9f7SBreno Leitao if args.input and not os.path.isfile(args.input): 345f061c9f7SBreno Leitao logging.warning("%s is not a valid file.", args.input) 346f061c9f7SBreno Leitao sys.exit(-1) 347f061c9f7SBreno Leitao 348f061c9f7SBreno Leitao if not args.output: 349f061c9f7SBreno Leitao logging.error("No output file specified.") 350f061c9f7SBreno Leitao sys.exit(-1) 351f061c9f7SBreno Leitao 352f061c9f7SBreno Leitao if os.path.isfile(args.output): 353f061c9f7SBreno Leitao logging.debug("%s already exists. Overwriting it.", args.output) 354f061c9f7SBreno Leitao 355f061c9f7SBreno Leitao return args 356f061c9f7SBreno Leitao 357f061c9f7SBreno Leitao 358f061c9f7SBreno Leitaodef parse_yaml_file(filename: str) -> str: 359f061c9f7SBreno Leitao """Transform the YAML specified by filename into a rst-formmated string""" 360f061c9f7SBreno Leitao with open(filename, "r", encoding="utf-8") as spec_file: 361f061c9f7SBreno Leitao yaml_data = yaml.safe_load(spec_file) 362f061c9f7SBreno Leitao content = parse_yaml(yaml_data) 363f061c9f7SBreno Leitao 364f061c9f7SBreno Leitao return content 365f061c9f7SBreno Leitao 366f061c9f7SBreno Leitao 367f061c9f7SBreno Leitaodef write_to_rstfile(content: str, filename: str) -> None: 368f061c9f7SBreno Leitao """Write the generated content into an RST file""" 369f061c9f7SBreno Leitao logging.debug("Saving RST file to %s", filename) 370f061c9f7SBreno Leitao 371f061c9f7SBreno Leitao with open(filename, "w", encoding="utf-8") as rst_file: 372f061c9f7SBreno Leitao rst_file.write(content) 373f061c9f7SBreno Leitao 374f061c9f7SBreno Leitao 375f061c9f7SBreno Leitaodef generate_main_index_rst(output: str) -> None: 376f061c9f7SBreno Leitao """Generate the `networking_spec/index` content and write to the file""" 377f061c9f7SBreno Leitao lines = [] 378f061c9f7SBreno Leitao 379f061c9f7SBreno Leitao lines.append(rst_header()) 380e8c780a5SJakub Kicinski lines.append(rst_label("specs")) 381e8c780a5SJakub Kicinski lines.append(rst_title("Netlink Family Specifications")) 382f061c9f7SBreno Leitao lines.append(rst_toctree(1)) 383f061c9f7SBreno Leitao 384f061c9f7SBreno Leitao index_dir = os.path.dirname(output) 385f061c9f7SBreno Leitao logging.debug("Looking for .rst files in %s", index_dir) 386e8c32339SDonald Hunter for filename in sorted(os.listdir(index_dir)): 387f061c9f7SBreno Leitao if not filename.endswith(".rst") or filename == "index.rst": 388f061c9f7SBreno Leitao continue 389f061c9f7SBreno Leitao lines.append(f" {filename.replace('.rst', '')}\n") 390f061c9f7SBreno Leitao 391f061c9f7SBreno Leitao logging.debug("Writing an index file at %s", output) 392f061c9f7SBreno Leitao write_to_rstfile("".join(lines), output) 393f061c9f7SBreno Leitao 394f061c9f7SBreno Leitao 395f061c9f7SBreno Leitaodef main() -> None: 396f061c9f7SBreno Leitao """Main function that reads the YAML files and generates the RST files""" 397f061c9f7SBreno Leitao 398f061c9f7SBreno Leitao args = parse_arguments() 399f061c9f7SBreno Leitao 400f061c9f7SBreno Leitao if args.input: 401f061c9f7SBreno Leitao logging.debug("Parsing %s", args.input) 402f061c9f7SBreno Leitao try: 403f061c9f7SBreno Leitao content = parse_yaml_file(os.path.join(args.input)) 404f061c9f7SBreno Leitao except Exception as exception: 405f061c9f7SBreno Leitao logging.warning("Failed to parse %s.", args.input) 406f061c9f7SBreno Leitao logging.warning(exception) 407f061c9f7SBreno Leitao sys.exit(-1) 408f061c9f7SBreno Leitao 409f061c9f7SBreno Leitao write_to_rstfile(content, args.output) 410f061c9f7SBreno Leitao 411f061c9f7SBreno Leitao if args.index: 412f061c9f7SBreno Leitao # Generate the index RST file 413f061c9f7SBreno Leitao generate_main_index_rst(args.output) 414f061c9f7SBreno Leitao 415f061c9f7SBreno Leitao 416f061c9f7SBreno Leitaoif __name__ == "__main__": 417f061c9f7SBreno Leitao main() 418