-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcreate-docs.py
More file actions
171 lines (137 loc) · 6.7 KB
/
create-docs.py
File metadata and controls
171 lines (137 loc) · 6.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
import freephil_docs.markdown as phildoc
import os, sys
import re
import tomllib
import importlib
import inspect
import ast
import aares
import aares.datafiles
class JobClassFinder(ast.NodeVisitor):
"""AST visitor to find classes inheriting from 'Job', even with module imports."""
def __init__(self, job_module_name="aares.Job"):
self.job_module_name = job_module_name
self.job_classes = []
self.import_aliases = {}
def visit_Import(self, node):
"""Capture imports like 'import my_project.Job as JobAlias'."""
for alias in node.names:
if alias.name == self.job_module_name:
self.import_aliases[alias.asname or alias.name.split(".")[-1]] = self.job_module_name
def visit_ImportFrom(self, node):
"""Capture imports like 'from my_project import Job'."""
if node.module == ".".join(self.job_module_name.split(".")[:-1]):
for alias in node.names:
if alias.name == self.job_module_name.split(".")[-1]:
self.import_aliases[alias.asname or alias.name] = self.job_module_name
def visit_ClassDef(self, node):
"""Detect classes inheriting from 'Job', considering import aliases."""
for base in node.bases:
if isinstance(base, ast.Name) and base.id in self.import_aliases:
if self.import_aliases[base.id] == self.job_module_name:
self.job_classes.append(node.name)
elif isinstance(base, ast.Attribute):
# Handle cases like 'my_project.Job'
full_name = f"{base.value.id}.{base.attr}" if isinstance(base.value, ast.Name) else None
if full_name == self.job_module_name:
self.job_classes.append(node.name)
self.generic_visit(node)
def find_descendant_classes(file_path, job_module_name="aares.Job"):
"""Find classes in the file that inherit from the base class 'Job'."""
with open(file_path, "r") as f:
tree = ast.parse(f.read(), filename=file_path)
finder = JobClassFinder(job_module_name)
finder.visit(tree)
return finder.job_classes
def extract_functions_from_pyproject(pyproject_path, job_module_name="aares.Job"):
# Parse the pyproject.toml file
with open(pyproject_path, "rb") as file:
pyproject_data = tomllib.load(file)
# Get the scripts section
scripts = pyproject_data['project']['scripts']
if not scripts:
raise ValueError("No scripts section found in pyproject.toml.")
results = {}
for script_name, entry_point in scripts.items():
module_name, _ = entry_point.split(":")
module_path = module_name.replace(".", os.sep) + ".py"
if os.path.exists(module_path):
# Step 1: Detect descendant classes statically
descendant_classes = find_descendant_classes(module_path, job_module_name)
# Step 2: Dynamically import and retrieve class objects
class_objects = []
try:
module = importlib.import_module(module_name)
for cls_name in descendant_classes:
cls = getattr(module, cls_name, None)
if cls and inspect.isclass(cls):
# Verify if the class inherits from 'Job'
base_classes = [base.__name__ for base in inspect.getmro(cls)]
if "Job" in base_classes:
class_objects.append(cls)
else:
print(f"Class {cls_name} in {module_name} is not a descendant of Job.")
else:
print(f"Class {cls_name} not found in module {module_name}.")
except ImportError as e:
print(f"Error importing module {module_name}: {e}")
# Store the class objects in the results
if class_objects:
results[script_name] = {
"module_name": module_name,
"classes": class_objects, # List of class objects
}
else:
print(f"Warning: {module_path} does not exist.")
return results
def create_docs(pyproject_path = "pyproject.toml", job_module_name="aares.Job", out_path = "wiki", tools_path=None):
extracted = extract_functions_from_pyproject(pyproject_path, job_module_name)
aares.create_directory(out_path)
if tools_path is None:
tools_path = "Tools"
full_tools_path = os.path.join(out_path, tools_path)
aares.create_directory(full_tools_path)
#list of tools
with open(os.path.join(out_path,"List_of_tools.md"),'w') as flist:
flist.write('''
AAres tools
===========
''')
for script_name, data in sorted(extracted.items()):
for cls in data["classes"]:
print(script_name)
flist.write(f'* **[{script_name}]({tools_path}/{script_name}.md)**: {cls.short_description}\n')
# tools PHIL documentation
for script_name, data in extracted.items():
fi_name = f"{script_name}.md"
with open(os.path.join(full_tools_path,fi_name),'w') as ftool:
cls = data["classes"][0]
if cls.system_phil == '':
continue
md = phildoc.phil_to_markdown(cls.system_phil,
title= script_name,
description= cls.long_description,
default_scope_description=True)
ftool.write(md)
ftool.write('\n---------\n\n## Full PHIL\n\n')
ftool.write('```\n')
ftool.write(cls.system_phil.as_str(attributes_level=0, expert_level=3))
ftool.write('```\n')
# ftool.write(f'# {script_name}\n')
# ftool.write('#'*len(script_name)+'\n\n')
# ftool.write(data['classes'][0].long_description)
# ftool.write('\n')
# FLS file documentation
with open(os.path.join(out_path,'FLS_file.md'), 'w') as flist:
md = phildoc.phil_to_markdown(aares.datafiles.phil_files,
title= 'FLS file description',
description='This file holds user accessible/editable information about the data files in use. The files are divided into groups by common experimental geometry. The order of files within the group does not matter.',
default_scope_description=True)
flist.write(md)
flist.write('\n---------\n\n## Full PHIL\n\n')
flist.write('```\n')
flist.write(aares.datafiles.phil_files.as_str(attributes_level=0, expert_level=3))
flist.write('```\n')
if __name__ == "__main__":
create_docs(out_path='wiki')
sys.exit(0)