1+ import ast
12import logging
23import shutil
34from pathlib import Path
4- from typing import Any , Dict , List , Union
5+ from typing import Any , Dict , List , Optional , Tuple , Union
56
67import yaml
78
@@ -153,6 +154,94 @@ def copy_assets() -> None:
153154 log .warning (f"Source: { file_or_dir } not found, skipping." )
154155
155156
157+ def find_service_settings_classes (src_dir : Path ) -> List [Tuple [str , Optional [str ]]]: # noqa: C901
158+ """
159+ Scan all Python files in the source directory to find classes that inherit from ServiceSettings.
160+
161+ Returns a list of tuples: (class_name, yml_section_value)
162+ """
163+ service_settings : List [Tuple [str , Optional [str ]]] = []
164+
165+ for py_file in src_dir .rglob ("*.py" ):
166+ try :
167+ with open (py_file , "r" , encoding = "utf-8" ) as f :
168+ tree = ast .parse (f .read (), filename = str (py_file ))
169+
170+ for node in ast .walk (tree ):
171+ if isinstance (node , ast .ClassDef ):
172+ # Check if class inherits from ServiceSettings
173+ inherits_from_service_settings = False
174+ for base in node .bases :
175+ if isinstance (base , ast .Name ) and base .id == "ServiceSettings" :
176+ inherits_from_service_settings = True
177+ break
178+
179+ if not inherits_from_service_settings :
180+ continue
181+
182+ # Look for __yml_section__ attribute
183+ yml_section : Optional [str ] = None
184+ for item in node .body :
185+ # Handle annotated assignments: __yml_section__: ClassVar[str] = "value"
186+ if isinstance (item , ast .AnnAssign ):
187+ if isinstance (item .target , ast .Name ) and item .target .id == "__yml_section__" :
188+ if item .value and isinstance (item .value , ast .Constant ):
189+ yml_section = item .value .value
190+ break
191+ # Handle simple assignments: __yml_section__ = "value"
192+ elif isinstance (item , ast .Assign ):
193+ for target in item .targets :
194+ if isinstance (target , ast .Name ) and target .id == "__yml_section__" :
195+ if isinstance (item .value , ast .Constant ):
196+ yml_section = item .value .value
197+ break
198+ if yml_section is not None :
199+ break
200+
201+ service_settings .append ((node .name , yml_section ))
202+
203+ except Exception as e :
204+ log .warning (f"Failed to parse { py_file } : { e } " )
205+
206+ return sorted (service_settings , key = lambda x : x [0 ])
207+
208+
209+ def generate_service_settings_table () -> None :
210+ """
211+ Generate a markdown table of ServiceSettings classes and their yml_section values.
212+ """
213+ log .info ("Generating service settings documentation..." )
214+
215+ service_settings = find_service_settings_classes (SRC_DIR )
216+
217+ if not service_settings :
218+ log .warning ("No ServiceSettings classes found." )
219+ return
220+
221+ # Build markdown content
222+ content = [
223+ "# Service Settings" ,
224+ "" ,
225+ "This table lists all service settings classes and their YAML section names." ,
226+ "" ,
227+ ]
228+ content .append ("| Class Name | YAML Section |" )
229+ content .append ("|------------|--------------|" )
230+
231+ for class_name , yml_section in service_settings :
232+ section_display = yml_section if yml_section else "None"
233+ content .append (f"| `{ class_name } ` | `{ section_display } ` |" )
234+
235+ # Write to file in articles directory
236+ articles_dir = DOCS_DIR / "articles"
237+ articles_dir .mkdir (exist_ok = True )
238+ output_path = articles_dir / "service_settings.md"
239+ with open (output_path , "w" , encoding = "utf-8" ) as f :
240+ f .write ("\n " .join (content ))
241+
242+ log .info (f"Service settings documentation written to { output_path } " )
243+
244+
156245def main () -> None :
157246 log .info ("Starting API documentation regeneration..." )
158247 copy_assets ()
@@ -163,6 +252,9 @@ def main() -> None:
163252 # Update mkdocs.yml
164253 update_mkdocs_yml (api_structure )
165254
255+ # Generate service settings table
256+ generate_service_settings_table ()
257+
166258 log .info ("API documentation regenerated successfully." )
167259
168260
0 commit comments