Logo AND Algorithmique Numérique Distribuée

Public GIT Repository
codefactor: fix unnecessary else after return maintainability issue
[simgrid.git] / docs / find-missing.py
1 #! /usr/bin/env python3
2 # -*- coding: utf-8 -*-
3
4 # Copyright (c) 2019. The SimGrid Team.
5 # All rights reserved.
6
7 # This program is free software; you can redistribute it and/or modify it
8 # under the terms of the license (GNU LGPL) which comes with this package.
9
10 """
11 Search for symbols documented in both the XML files produced by Doxygen and the python modules,
12 but not documented with autodoxy in the RST files.
13
14 This script is tailored to SimGrid own needs and should be made more generic for autodoxy.
15 """
16
17 import fnmatch
18 import os
19 import re
20 import sys
21 import xml.etree.ElementTree as ET
22 import inspect
23
24 xml_files = [
25     'build/xml/classsimgrid_1_1s4u_1_1Activity.xml',
26     'build/xml/classsimgrid_1_1s4u_1_1Actor.xml',
27     'build/xml/classsimgrid_1_1s4u_1_1Barrier.xml',
28     'build/xml/classsimgrid_1_1s4u_1_1Comm.xml',
29     'build/xml/classsimgrid_1_1s4u_1_1ConditionVariable.xml',
30     'build/xml/classsimgrid_1_1s4u_1_1Disk.xml',
31     'build/xml/classsimgrid_1_1s4u_1_1Engine.xml',
32     'build/xml/classsimgrid_1_1s4u_1_1ExecPar.xml',
33     'build/xml/classsimgrid_1_1s4u_1_1ExecSeq.xml',
34     'build/xml/classsimgrid_1_1s4u_1_1Exec.xml',
35     'build/xml/classsimgrid_1_1s4u_1_1Host.xml',
36     'build/xml/classsimgrid_1_1s4u_1_1Io.xml',
37     'build/xml/classsimgrid_1_1s4u_1_1Link.xml',
38     'build/xml/classsimgrid_1_1s4u_1_1Mailbox.xml',
39     'build/xml/classsimgrid_1_1s4u_1_1Mutex.xml',
40     'build/xml/classsimgrid_1_1s4u_1_1NetZone.xml',
41     'build/xml/classsimgrid_1_1s4u_1_1Semaphore.xml',
42     'build/xml/classsimgrid_1_1s4u_1_1VirtualMachine.xml'
43 ]
44
45 python_modules = [
46     'simgrid'
47 ]
48 python_ignore = [
49     'simgrid.ActorKilled'
50 ]
51
52
53 ############ Search the python elements in the source, and report undocumented ones
54 ############
55
56 # data structure in which we store the declaration we find
57 python_decl = {}
58
59 def handle_python_module(fullname, englobing, elm):
60     """Recursive function exploring the python modules."""
61
62     def found_decl(kind, obj):
63         """Helper function that add an object in the python_decl data structure"""
64         if kind not in python_decl:
65             python_decl[kind] = []
66         python_decl[kind].append(obj)
67
68
69     if fullname in python_ignore:
70         print ("Ignore Python symbol '{}' as requested.".format(fullname))
71         return
72
73     if inspect.isroutine(elm) and inspect.isclass(englobing):
74         found_decl("method", fullname)
75 #        print('.. automethod:: {}'.format(fullname))
76     elif inspect.isroutine(elm) and (not inspect.isclass(englobing)):
77         found_decl("function", fullname)
78 #        print('.. autofunction:: {}'.format(fullname))
79     elif inspect.isdatadescriptor(elm):
80         found_decl("attribute", fullname)
81 #        print('.. autoattribute:: {}'.format(fullname))
82     elif isinstance(elm, str) or isinstance(elm, int): # We do have such a data, directly in the SimGrid top module
83         found_decl("data", fullname)
84 #        print('.. autodata:: {}'.format(fullname))
85     elif inspect.ismodule(elm) or inspect.isclass(elm):
86         for name, data in inspect.getmembers(elm):
87             if name.startswith('__'):
88                 continue
89 #            print("Recurse on {}.{}".format(fullname, name))
90             handle_python_module("{}.{}".format(fullname, name), elm, data)
91     else:
92         print('UNHANDLED TYPE {} : {!r} Type: {}'.format(fullname, elm, type(elm)))
93
94 # Start the recursion on the provided Python modules
95 for name in python_modules:
96     try:
97         module = __import__(name)
98     except Exception:
99         print("Cannot import {}. Did you set PYTHONPATH=../lib accordingly?".format(name))
100         sys.exit(1)
101     for sub in dir(module):
102         if sub[0] == '_':
103             continue
104         handle_python_module("{}.{}".format(name, sub), module, getattr(module, sub))
105
106 # Forget about the python declarations that were actually done in the RST
107 for kind in python_decl:
108     with os.popen('grep \'[[:blank:]]*auto{}::\' source/*rst|sed \'s/^.*auto{}:: //\''.format(kind, kind)) as pse:
109         for fullname in (l.strip() for l in pse):
110             if fullname not in python_decl[kind]:
111                 print("Warning: {} documented but declaration not found in python.".format(fullname))
112             else:
113                 python_decl[kind].remove(fullname)
114 # Dump the missing ones
115 for kind in python_decl:
116     for fullname in python_decl[kind]:
117         print(" .. auto{}:: {}".format(kind, fullname))
118
119 ################ And now deal with Doxygen declarations
120 ################
121
122 doxy_funs = {} # {classname: {func_name: [args]} }
123 doxy_vars = {} # {classname: [names]}
124
125 # find the declarations in the XML files
126 for arg in xml_files[:3]:
127     if arg[-4:] != '.xml':
128         print ("Argument '{}' does not end with '.xml'".format(arg))
129         continue
130     print("Parse file {}".format(arg))
131     tree = ET.parse(arg)
132     for elem in tree.findall(".//compounddef"):
133         if elem.attrib["prot"] != "public":
134             continue
135         if "compoundname" in elem:
136             raise Exception("Compound {} has no 'compoundname' child tag.".format(elem))
137         compoundname = elem.find("compoundname").text
138         #print ("compoundname {}".format(compoundname))
139         for member in elem.findall('.//memberdef'):
140             if member.attrib["prot"] != "public":
141                 continue
142             kind = member.attrib["kind"]
143             name = member.find("name").text
144             if kind == "variable":
145                 if compoundname not in doxy_vars:
146                     doxy_vars[compoundname] = []
147                 doxy_vars[compoundname].append(name)
148             elif kind == "function":
149                 args = member.find('argsstring').text
150                 args = re.sub('\)[^)]*$', ')', args) # ignore what's after the parameters (eg, '=0' or ' const')
151
152                 if compoundname not in doxy_funs:
153                     doxy_funs[compoundname] = {}
154                 if name not in doxy_funs[compoundname]:
155                     doxy_funs[compoundname][name] = []
156                 doxy_funs[compoundname][name].append(args)
157             else:
158                 print ("member {}::{} is of kind {}".format(compoundname, name, kind))
159
160 # Forget about the declarations that are done in the RST
161 with os.popen('grep autodoxymethod:: source/*rst|sed \'s/^.*autodoxymethod:: //\'') as pse:
162     for line in (l.strip() for l in pse):
163         (klass, obj, args) = (None, None, None)
164         if "(" in line:
165             (line, args) = line.split('(', 1)
166             args = "({}".format(args)
167         (klass, obj) = line.rsplit('::', 1)
168
169         if klass not in doxy_funs:
170             print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
171             continue
172         if obj not in doxy_funs[klass]:
173             print("Warning: Object {} documented but not found in {}".format(line, klass))
174         elif len(doxy_funs[klass][obj])==1:
175             del doxy_funs[klass][obj]
176         elif args not in doxy_funs[klass][obj]:
177             print("Warning: Function {}{} not found in {}".format(obj, args, klass))
178         else:
179 #            print("Found {} in {}".format(line, klass))
180             doxy_funs[klass][obj].remove(args)
181             if len(doxy_funs[klass][obj]) == 0:
182                 del doxy_funs[klass][obj]
183 with os.popen('grep autodoxyvar:: source/*rst|sed \'s/^.*autodoxyvar:: //\'') as pse:
184     for line in (l.strip() for l in pse):
185         (klass, var) = line.rsplit('::', 1)
186
187         if klass not in doxy_vars:
188             print("Warning: {} documented, but class {} not found in doxygen.".format(line, klass))
189             continue
190         if var not in doxy_vars[klass]:
191             print("Warning: Object {} documented but not found in {}".format(line, klass))
192         else:
193 #            print("Found {} in {}".format(line, klass))
194             doxy_vars[klass].remove(var)
195             if len(doxy_vars[klass]) == 0:
196                 del doxy_vars[klass]
197
198 # Dump the undocumented Doxygen declarations 
199 for obj in sorted(doxy_funs):
200     for meth in sorted(doxy_funs[obj]):
201         for args in sorted(doxy_funs[obj][meth]):
202             print(".. autodoxymethod:: {}::{}{}".format(obj, meth, args))
203
204 for obj in doxy_vars:
205     for meth in sorted(doxy_vars[obj]):
206         print(".. autodoxyvar:: {}::{}".format(obj, meth))