~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/net/ynl/ynl-gen-rst.py

Version: ~ [ linux-6.12-rc7 ] ~ [ linux-6.11.7 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.60 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.116 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.171 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.229 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.285 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.323 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.12 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

Diff markup

Differences between /tools/net/ynl/ynl-gen-rst.py (Version linux-6.12-rc7) and /tools/net/ynl/ynl-gen-rst.py (Version linux-6.0.19)


  1 #!/usr/bin/env python3                            
  2 # SPDX-License-Identifier: GPL-2.0                
  3 # -*- coding: utf-8; mode: python -*-             
  4                                                   
  5 """                                               
  6     Script to auto generate the documentation     
  7                                                   
  8     :copyright:  Copyright (C) 2023  Breno Lei<    
  9     :license:    GPL Version 2, June 1991 see     
 10                                                   
 11     This script performs extensive parsing to     
 12     spec files, in an effort to avoid needing     
 13     YAML file.                                    
 14                                                   
 15     This code is split in three big parts:        
 16         1) RST formatters: Use to convert a st    
 17         2) Parser helpers: Functions to parse     
 18         3) Main function and small helpers        
 19 """                                               
 20                                                   
 21 from typing import Any, Dict, List                
 22 import os.path                                    
 23 import sys                                        
 24 import argparse                                   
 25 import logging                                    
 26 import yaml                                       
 27                                                   
 28                                                   
 29 SPACE_PER_LEVEL = 4                               
 30                                                   
 31                                                   
 32 # RST Formatters                                  
 33 # ==============                                  
 34 def headroom(level: int) -> str:                  
 35     """Return space to format"""                  
 36     return " " * (level * SPACE_PER_LEVEL)        
 37                                                   
 38                                                   
 39 def bold(text: str) -> str:                       
 40     """Format bold text"""                        
 41     return f"**{text}**"                          
 42                                                   
 43                                                   
 44 def inline(text: str) -> str:                     
 45     """Format inline text"""                      
 46     return f"``{text}``"                          
 47                                                   
 48                                                   
 49 def sanitize(text: str) -> str:                   
 50     """Remove newlines and multiple spaces"""     
 51     # This is useful for some fields that are     
 52     return str(text).replace("\n", " ").strip(    
 53                                                   
 54                                                   
 55 def rst_fields(key: str, value: str, level: in    
 56     """Return a RST formatted field"""            
 57     return headroom(level) + f":{key}: {value}    
 58                                                   
 59                                                   
 60 def rst_definition(key: str, value: Any, level    
 61     """Format a single rst definition"""          
 62     return headroom(level) + key + "\n" + head    
 63                                                   
 64                                                   
 65 def rst_paragraph(paragraph: str, level: int =    
 66     """Return a formatted paragraph"""            
 67     return headroom(level) + paragraph            
 68                                                   
 69                                                   
 70 def rst_bullet(item: str, level: int = 0) -> s    
 71     """Return a formatted a bullet"""             
 72     return headroom(level) + f"- {item}"          
 73                                                   
 74                                                   
 75 def rst_subsection(title: str) -> str:            
 76     """Add a sub-section to the document"""       
 77     return f"{title}\n" + "-" * len(title)        
 78                                                   
 79                                                   
 80 def rst_subsubsection(title: str) -> str:         
 81     """Add a sub-sub-section to the document""    
 82     return f"{title}\n" + "~" * len(title)        
 83                                                   
 84                                                   
 85 def rst_section(namespace: str, prefix: str, t    
 86     """Add a section to the document"""           
 87     return f".. _{namespace}-{prefix}-{title}:    
 88                                                   
 89                                                   
 90 def rst_subtitle(title: str) -> str:              
 91     """Add a subtitle to the document"""          
 92     return "\n" + "-" * len(title) + f"\n{titl    
 93                                                   
 94                                                   
 95 def rst_title(title: str) -> str:                 
 96     """Add a title to the document"""             
 97     return "=" * len(title) + f"\n{title}\n" +    
 98                                                   
 99                                                   
100 def rst_list_inline(list_: List[str], level: i    
101     """Format a list using inlines"""             
102     return headroom(level) + "[" + ", ".join(i    
103                                                   
104                                                   
105 def rst_ref(namespace: str, prefix: str, name:    
106     """Add a hyperlink to the document"""         
107     mappings = {'enum': 'definition',             
108                 'fixed-header': 'definition',     
109                 'nested-attributes': 'attribut    
110                 'struct': 'definition'}           
111     if prefix in mappings:                        
112         prefix = mappings[prefix]                 
113     return f":ref:`{namespace}-{prefix}-{name}    
114                                                   
115                                                   
116 def rst_header() -> str:                          
117     """The headers for all the auto generated     
118     lines = []                                    
119                                                   
120     lines.append(rst_paragraph(".. SPDX-Licens    
121     lines.append(rst_paragraph(".. NOTE: This     
122                                                   
123     return "\n".join(lines)                       
124                                                   
125                                                   
126 def rst_toctree(maxdepth: int = 2) -> str:        
127     """Generate a toctree RST primitive"""        
128     lines = []                                    
129                                                   
130     lines.append(".. toctree::")                  
131     lines.append(f"   :maxdepth: {maxdepth}\n\    
132                                                   
133     return "\n".join(lines)                       
134                                                   
135                                                   
136 def rst_label(title: str) -> str:                 
137     """Return a formatted label"""                
138     return f".. _{title}:\n\n"                    
139                                                   
140                                                   
141 # Parsers                                         
142 # =======                                         
143                                                   
144                                                   
145 def parse_mcast_group(mcast_group: List[Dict[s    
146     """Parse 'multicast' group list and return    
147     lines = []                                    
148     for group in mcast_group:                     
149         lines.append(rst_bullet(group["name"])    
150                                                   
151     return "\n".join(lines)                       
152                                                   
153                                                   
154 def parse_do(do_dict: Dict[str, Any], level: i    
155     """Parse 'do' section and return a formatt    
156     lines = []                                    
157     for key in do_dict.keys():                    
158         lines.append(rst_paragraph(bold(key),     
159         if key in ['request', 'reply']:           
160             lines.append(parse_do_attributes(d    
161         else:                                     
162             lines.append(headroom(level + 2) +    
163                                                   
164     return "\n".join(lines)                       
165                                                   
166                                                   
167 def parse_do_attributes(attrs: Dict[str, Any],    
168     """Parse 'attributes' section"""              
169     if "attributes" not in attrs:                 
170         return ""                                 
171     lines = [rst_fields("attributes", rst_list    
172                                                   
173     return "\n".join(lines)                       
174                                                   
175                                                   
176 def parse_operations(operations: List[Dict[str    
177     """Parse operations block"""                  
178     preprocessed = ["name", "doc", "title", "d    
179     linkable = ["fixed-header", "attribute-set    
180     lines = []                                    
181                                                   
182     for operation in operations:                  
183         lines.append(rst_section(namespace, 'o    
184         lines.append(rst_paragraph(operation["    
185                                                   
186         for key in operation.keys():              
187             if key in preprocessed:               
188                 # Skip the special fields         
189                 continue                          
190             value = operation[key]                
191             if key in linkable:                   
192                 value = rst_ref(namespace, key    
193             lines.append(rst_fields(key, value    
194         if 'flags' in operation:                  
195             lines.append(rst_fields('flags', r    
196                                                   
197         if "do" in operation:                     
198             lines.append(rst_paragraph(":do:",    
199             lines.append(parse_do(operation["d    
200         if "dump" in operation:                   
201             lines.append(rst_paragraph(":dump:    
202             lines.append(parse_do(operation["d    
203                                                   
204         # New line after fields                   
205         lines.append("\n")                        
206                                                   
207     return "\n".join(lines)                       
208                                                   
209                                                   
210 def parse_entries(entries: List[Dict[str, Any]    
211     """Parse a list of entries"""                 
212     ignored = ["pad"]                             
213     lines = []                                    
214     for entry in entries:                         
215         if isinstance(entry, dict):               
216             # entries could be a list or a dic    
217             field_name = entry.get("name", "")    
218             if field_name in ignored:             
219                 continue                          
220             type_ = entry.get("type")             
221             if type_:                             
222                 field_name += f" ({inline(type    
223             lines.append(                         
224                 rst_fields(field_name, sanitiz    
225             )                                     
226         elif isinstance(entry, list):             
227             lines.append(rst_list_inline(entry    
228         else:                                     
229             lines.append(rst_bullet(inline(san    
230                                                   
231     lines.append("\n")                            
232     return "\n".join(lines)                       
233                                                   
234                                                   
235 def parse_definitions(defs: Dict[str, Any], na    
236     """Parse definitions section"""               
237     preprocessed = ["name", "entries", "member    
238     ignored = ["render-max"]  # This is not pr    
239     lines = []                                    
240                                                   
241     for definition in defs:                       
242         lines.append(rst_section(namespace, 'd    
243         for k in definition.keys():               
244             if k in preprocessed + ignored:       
245                 continue                          
246             lines.append(rst_fields(k, sanitiz    
247                                                   
248         # Field list needs to finish with a ne    
249         lines.append("\n")                        
250         if "entries" in definition:               
251             lines.append(rst_paragraph(":entri    
252             lines.append(parse_entries(definit    
253         if "members" in definition:               
254             lines.append(rst_paragraph(":membe    
255             lines.append(parse_entries(definit    
256                                                   
257     return "\n".join(lines)                       
258                                                   
259                                                   
260 def parse_attr_sets(entries: List[Dict[str, An    
261     """Parse attribute from attribute-set"""      
262     preprocessed = ["name", "type"]               
263     linkable = ["enum", "nested-attributes", "    
264     ignored = ["checks"]                          
265     lines = []                                    
266                                                   
267     for entry in entries:                         
268         lines.append(rst_section(namespace, 'a    
269         for attr in entry["attributes"]:          
270             type_ = attr.get("type")              
271             attr_line = attr["name"]              
272             if type_:                             
273                 # Add the attribute type in th    
274                 attr_line += f" ({inline(type_    
275                                                   
276             lines.append(rst_subsubsection(att    
277                                                   
278             for k in attr.keys():                 
279                 if k in preprocessed + ignored    
280                     continue                      
281                 if k in linkable:                 
282                     value = rst_ref(namespace,    
283                 else:                             
284                     value = sanitize(attr[k])     
285                 lines.append(rst_fields(k, val    
286             lines.append("\n")                    
287                                                   
288     return "\n".join(lines)                       
289                                                   
290                                                   
291 def parse_sub_messages(entries: List[Dict[str,    
292     """Parse sub-message definitions"""           
293     lines = []                                    
294                                                   
295     for entry in entries:                         
296         lines.append(rst_section(namespace, 's    
297         for fmt in entry["formats"]:              
298             value = fmt["value"]                  
299                                                   
300             lines.append(rst_bullet(bold(value    
301             for attr in ['fixed-header', 'attr    
302                 if attr in fmt:                   
303                     lines.append(rst_fields(at    
304                                             rs    
305                                             1)    
306             lines.append("\n")                    
307                                                   
308     return "\n".join(lines)                       
309                                                   
310                                                   
311 def parse_yaml(obj: Dict[str, Any]) -> str:       
312     """Format the whole YAML into a RST string    
313     lines = []                                    
314                                                   
315     # Main header                                 
316                                                   
317     lines.append(rst_header())                    
318                                                   
319     family = obj['name']                          
320                                                   
321     title = f"Family ``{family}`` netlink spec    
322     lines.append(rst_title(title))                
323     lines.append(rst_paragraph(".. contents::     
324                                                   
325     if "doc" in obj:                              
326         lines.append(rst_subtitle("Summary"))     
327         lines.append(rst_paragraph(obj["doc"],    
328                                                   
329     # Operations                                  
330     if "operations" in obj:                       
331         lines.append(rst_subtitle("Operations"    
332         lines.append(parse_operations(obj["ope    
333                                                   
334     # Multicast groups                            
335     if "mcast-groups" in obj:                     
336         lines.append(rst_subtitle("Multicast g    
337         lines.append(parse_mcast_group(obj["mc    
338                                                   
339     # Definitions                                 
340     if "definitions" in obj:                      
341         lines.append(rst_subtitle("Definitions    
342         lines.append(parse_definitions(obj["de    
343                                                   
344     # Attributes set                              
345     if "attribute-sets" in obj:                   
346         lines.append(rst_subtitle("Attribute s    
347         lines.append(parse_attr_sets(obj["attr    
348                                                   
349     # Sub-messages                                
350     if "sub-messages" in obj:                     
351         lines.append(rst_subtitle("Sub-message    
352         lines.append(parse_sub_messages(obj["s    
353                                                   
354     return "\n".join(lines)                       
355                                                   
356                                                   
357 # Main functions                                  
358 # ==============                                  
359                                                   
360                                                   
361 def parse_arguments() -> argparse.Namespace:      
362     """Parse arguments from user"""               
363     parser = argparse.ArgumentParser(descripti    
364                                                   
365     parser.add_argument("-v", "--verbose", act    
366     parser.add_argument("-o", "--output", help    
367                                                   
368     # Index and input are mutually exclusive      
369     group = parser.add_mutually_exclusive_grou    
370     group.add_argument(                           
371         "-x", "--index", action="store_true",     
372     )                                             
373     group.add_argument("-i", "--input", help="    
374                                                   
375     args = parser.parse_args()                    
376                                                   
377     if args.verbose:                              
378         logging.basicConfig(level=logging.DEBU    
379                                                   
380     if args.input and not os.path.isfile(args.    
381         logging.warning("%s is not a valid fil    
382         sys.exit(-1)                              
383                                                   
384     if not args.output:                           
385         logging.error("No output file specifie    
386         sys.exit(-1)                              
387                                                   
388     if os.path.isfile(args.output):               
389         logging.debug("%s already exists. Over    
390                                                   
391     return args                                   
392                                                   
393                                                   
394 def parse_yaml_file(filename: str) -> str:        
395     """Transform the YAML specified by filenam    
396     with open(filename, "r", encoding="utf-8")    
397         yaml_data = yaml.safe_load(spec_file)     
398         content = parse_yaml(yaml_data)           
399                                                   
400     return content                                
401                                                   
402                                                   
403 def write_to_rstfile(content: str, filename: s    
404     """Write the generated content into an RST    
405     logging.debug("Saving RST file to %s", fil    
406                                                   
407     with open(filename, "w", encoding="utf-8")    
408         rst_file.write(content)                   
409                                                   
410                                                   
411 def generate_main_index_rst(output: str) -> No    
412     """Generate the `networking_spec/index` co    
413     lines = []                                    
414                                                   
415     lines.append(rst_header())                    
416     lines.append(rst_label("specs"))              
417     lines.append(rst_title("Netlink Family Spe    
418     lines.append(rst_toctree(1))                  
419                                                   
420     index_dir = os.path.dirname(output)           
421     logging.debug("Looking for .rst files in %    
422     for filename in sorted(os.listdir(index_di    
423         if not filename.endswith(".rst") or fi    
424             continue                              
425         lines.append(f"   {filename.replace('.    
426                                                   
427     logging.debug("Writing an index file at %s    
428     write_to_rstfile("".join(lines), output)      
429                                                   
430                                                   
431 def main() -> None:                               
432     """Main function that reads the YAML files    
433                                                   
434     args = parse_arguments()                      
435                                                   
436     if args.input:                                
437         logging.debug("Parsing %s", args.input    
438         try:                                      
439             content = parse_yaml_file(os.path.    
440         except Exception as exception:            
441             logging.warning("Failed to parse %    
442             logging.warning(exception)            
443             sys.exit(-1)                          
444                                                   
445         write_to_rstfile(content, args.output)    
446                                                   
447     if args.index:                                
448         # Generate the index RST file             
449         generate_main_index_rst(args.output)      
450                                                   
451                                                   
452 if __name__ == "__main__":                        
453     main()                                        
                                                      

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php