Merge branch 'blender2.7'
[blender.git] / tests / python / bl_rst_completeness.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20
21 # run this script in the game engine.
22 # or on the command line with...
23 #  ./blender.bin --background -noaudio --python tests/python/bl_rst_completeness.py
24 '''
25 filepath = "/src/blender/tests/python/bl_rst_completeness.py"
26 exec(compile(open(filepath).read(), filepath, 'exec'))
27 '''
28
29 import os
30
31 THIS_DIR = os.path.dirname(__file__)
32 RST_DIR = os.path.normpath(os.path.join(THIS_DIR, "..", "..", "doc", "python_api", "rst"))
33
34 import sys
35 sys.path.append(THIS_DIR)
36
37 import rst_to_doctree_mini
38
39
40 # (file, module)
41 modules = (
42     ("bgl.rst", "bgl", True),
43     ("gpu.rst", "gpu", False),
44 )
45
46
47 def is_directive_pydata(filepath, directive):
48     if directive.type in {"function", "method", "class", "attribute", "data"}:
49         return True
50     elif directive.type in {"module", "note", "warning", "code-block", "hlist", "seealso"}:
51         return False
52     elif directive.type == "literalinclude":  # TODO
53         return False
54     else:
55         print(directive_to_str(filepath, directive), end=" ")
56         print("unknown directive type %r" % directive.type)
57         return False
58
59
60 def directive_to_str(filepath, directive):
61     return "%s:%d:%d:" % (filepath, directive.line + 1, directive.indent)
62
63
64 def directive_members_dict(filepath, directive_members):
65     return {directive.value_strip: directive for directive in directive_members
66             if is_directive_pydata(filepath, directive)}
67
68
69 def module_validate(filepath, mod, mod_name, doctree, partial_ok):
70     # RST member missing from MODULE ???
71     for directive in doctree:
72         # print(directive.type)
73         if is_directive_pydata(filepath, directive):
74             attr = directive.value_strip
75             has_attr = hasattr(mod, attr)
76             ok = False
77             if not has_attr:
78                 # so we can have glNormal docs cover glNormal3f
79                 if partial_ok:
80                     for s in dir(mod):
81                         if s.startswith(attr):
82                             ok = True
83                             break
84
85                 if not ok:
86                     print(directive_to_str(filepath, directive), end=" ")
87                     print("rst contains non existing member %r" % attr)
88
89             # if its a class, scan down the class...
90             # print(directive.type)
91             if has_attr:
92                 if directive.type == "class":
93                     cls = getattr(mod, attr)
94                     # print("directive:      ", directive)
95                     for directive_child in directive.members:
96                         # print("directive_child: ", directive_child)
97                         if is_directive_pydata(filepath, directive_child):
98                             attr_child = directive_child.value_strip
99                             if attr_child not in cls.__dict__:
100                                 attr_id = "%s.%s" % (attr, attr_child)
101                                 print(directive_to_str(filepath, directive_child), end=" ")
102                                 print("rst contains non existing class member %r" % attr_id)
103
104     # MODULE member missing from RST ???
105     doctree_dict = directive_members_dict(filepath, doctree)
106     for attr in dir(mod):
107         if attr.startswith("_"):
108             continue
109
110         directive = doctree_dict.get(attr)
111         if directive is None:
112             print("module contains undocumented member %r from %r" % ("%s.%s" % (mod_name, attr), filepath))
113         else:
114             if directive.type == "class":
115                 directive_dict = directive_members_dict(filepath, directive.members)
116                 cls = getattr(mod, attr)
117                 for attr_child in cls.__dict__.keys():
118                     if attr_child.startswith("_"):
119                         continue
120                     if attr_child not in directive_dict:
121                         attr_id = "%s.%s.%s" % (mod_name, attr, attr_child), filepath
122                         print("module contains undocumented member %r from %r" % attr_id)
123
124
125 def main():
126
127     for filename, modname, partial_ok in modules:
128         filepath = os.path.join(RST_DIR, filename)
129         if not os.path.exists(filepath):
130             raise Exception("%r not found" % filepath)
131
132         doctree = rst_to_doctree_mini.parse_rst_py(filepath)
133         __import__(modname)
134         mod = sys.modules[modname]
135
136         module_validate(filepath, mod, modname, doctree, partial_ok)
137
138
139 if __name__ == "__main__":
140     main()