[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [52423] trunk/blender/source/tests: scripts to report missing py reference api docs.

Campbell Barton ideasman42 at gmail.com
Wed Nov 21 08:40:48 CET 2012


Revision: 52423
          http://projects.blender.org/scm/viewvc.php?view=rev&root=bf-blender&revision=52423
Author:   campbellbarton
Date:     2012-11-21 07:40:46 +0000 (Wed, 21 Nov 2012)
Log Message:
-----------
scripts to report missing py reference api docs. (simple RST parse and compare with python)

Added Paths:
-----------
    trunk/blender/source/tests/bl_rst_completeness.py
    trunk/blender/source/tests/rst_to_doctree_mini.py

Added: trunk/blender/source/tests/bl_rst_completeness.py
===================================================================
--- trunk/blender/source/tests/bl_rst_completeness.py	                        (rev 0)
+++ trunk/blender/source/tests/bl_rst_completeness.py	2012-11-21 07:40:46 UTC (rev 52423)
@@ -0,0 +1,159 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+# run this script in the game engine.
+# or on the command line with...
+#  ./blender.bin --background -noaudio --python source/tests/bl_rst_completeness.py
+
+# Paste this into the bge and run on an always actuator.
+'''
+filepath = "/dsk/data/src/blender/blender/source/tests/bl_rst_completeness.py"
+exec(compile(open(filepath).read(), filepath, 'exec'))
+'''
+
+import os
+THIS_DIR = os.path.dirname(__file__)
+RST_DIR = os.path.normpath(os.path.join(THIS_DIR, "..", "..", "doc", "python_api", "rst"))
+
+import sys
+sys.path.append(THIS_DIR)
+
+import rst_to_doctree_mini
+
+try:
+    import bge
+except:
+    bge = None
+
+# (file, module)
+modules = (
+    ("bge.constraints.rst", "bge.constraints", False),
+    ("bge.events.rst", "bge.events", False),
+    ("bge.logic.rst", "bge.logic", False),
+    ("bge.render.rst", "bge.render", False),
+    ("bge.texture.rst", "bge.texture", False),
+    ("bge.types.rst", "bge.types", False),
+
+    ("bgl.rst", "bgl", True),
+    ("gpu.rst", "gpu", False),
+)
+
+def is_directive_pydata(filepath, directive):
+    if directive.type in {"function", "method", "class", "attribute", "data"}:
+        return True
+    elif directive.type in {"module", "note", "warning", "code-block", "hlist", "seealso"}:
+        return False
+    elif directive.type in {"literalinclude"}:  # TODO
+        return False
+    else:
+        print(directive_to_str(filepath, directive), end=" ")
+        print("unknown directive type %r" % directive.type)
+        return False
+
+
+def directive_to_str(filepath, directive):
+    return "%s:%d:%d:" % (filepath, directive.line + 1, directive.indent)
+
+
+def directive_members_dict(filepath, directive_members):
+    return {directive.value_strip: directive for directive in directive_members
+            if is_directive_pydata(filepath, directive)}
+
+
+def module_validate(filepath, mod, mod_name, doctree, partial_ok):
+    # RST member missing from MODULE ???
+    for directive in doctree:
+        # print(directive.type)
+        if is_directive_pydata(filepath, directive):
+            attr = directive.value_strip
+            has_attr = hasattr(mod, attr)
+            ok = False
+            if not has_attr:
+                # so we can have glNormal docs cover glNormal3f
+                if partial_ok:
+                    for s in dir(mod):
+                        if s.startswith(attr):
+                            ok = True
+                            break
+
+                if not ok:
+                    print(directive_to_str(filepath, directive), end=" ")
+                    print("rst contains non existing member %r" % attr)
+
+            # if its a class, scan down the class...
+            # print(directive.type)
+            if has_attr:
+                if directive.type == "class":
+                    cls = getattr(mod, attr)
+                    # print("directive:      ", directive)
+                    for directive_child in directive.members:
+                        # print("directive_child: ", directive_child)
+                        if is_directive_pydata(filepath, directive_child):
+                            attr_child = directive_child.value_strip
+                            if attr_child not in cls.__dict__:
+                                attr_id = "%s.%s" % (attr, attr_child)
+                                print(directive_to_str(filepath, directive_child), end=" ")
+                                print("rst contains non existing class member %r" % attr_id)
+
+
+    # MODULE member missing from RST ???
+    doctree_dict = directive_members_dict(filepath, doctree)
+    for attr in dir(mod):
+        if attr.startswith("_"):
+            continue
+
+        directive = doctree_dict.get(attr)
+        if directive is None:
+            print("module contains undocumented member %r from %r" % ("%s.%s" % (mod_name, attr), filepath))
+        else:
+            if directive.type == "class":
+                directive_dict = directive_members_dict(filepath, directive.members)
+                cls = getattr(mod, attr)
+                for attr_child in cls.__dict__.keys():
+                    if attr_child.startswith("_"):
+                        continue
+                    if attr_child not in directive_dict:
+                        attr_id = "%s.%s.%s" % (mod_name, attr, attr_child), filepath
+                        print("module contains undocumented member %r from %r" % attr_id)
+
+
+def main():
+    
+    if bge is None:
+        print("Skipping BGE modules!")
+        continue
+
+    for filename, modname, partial_ok in modules:
+        if bge is None and modname.startswith("bge"):
+            continue
+
+        filepath = os.path.join(RST_DIR, filename)
+        if not os.path.exists(filepath):
+            raise Exception("%r not found" % filepath)
+
+        doctree = rst_to_doctree_mini.parse_rst_py(filepath)
+        __import__(modname)
+        mod = sys.modules[modname]
+        
+        module_validate(filepath, mod, modname, doctree, partial_ok)
+
+
+if __name__ == "__main__":
+    main()


Property changes on: trunk/blender/source/tests/bl_rst_completeness.py
___________________________________________________________________
Added: svn:eol-style
   + native

Added: trunk/blender/source/tests/rst_to_doctree_mini.py
===================================================================
--- trunk/blender/source/tests/rst_to_doctree_mini.py	                        (rev 0)
+++ trunk/blender/source/tests/rst_to_doctree_mini.py	2012-11-21 07:40:46 UTC (rev 52423)
@@ -0,0 +1,91 @@
+# ##### BEGIN GPL LICENSE BLOCK #####
+#
+#  This program is free software; you can redistribute it and/or
+#  modify it under the terms of the GNU General Public License
+#  as published by the Free Software Foundation; either version 2
+#  of the License, or (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software Foundation,
+#  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# <pep8 compliant>
+
+# Module with function to extract a doctree from an reStructuredText file.
+# Named 'Mini' because we only parse the minimum data needed to check
+# Python classes, methods and attributes match up to those in existing modules.
+# (To test for documentation completeness)
+
+# note: literalinclude's are not followed.
+# could be nice to add but not really needed either right now.
+
+import collections
+
+Directive = collections.namedtuple('Directive',
+                                   ("type",
+                                    "value",
+                                    "value_strip",
+                                    "line",
+                                    "indent",
+                                    "members"))
+
+
+def parse_rst_py(filepath):
+    import re
+
+    # Get the prefix assuming the line is lstrip()'d
+    # ..foo:: bar
+    # -->
+    # ("foo", "bar")
+    re_prefix = re.compile(r"^\.\.\s([a-zA-Z09\-]+)::\s*(.*)\s*$")
+    
+    tree = collections.defaultdict(list)
+    
+    f = open(filepath, encoding="utf-8")
+    indent_lists = []
+    for i, line in enumerate(f):
+        line_strip = line.lstrip()
+        # ^\.\.\s[a-zA-Z09\-]+::.*$
+        #if line.startswith(".. "):
+        march = re_prefix.match(line_strip)
+
+        if march:
+            directive, value = march.group(1, 2)
+            indent = len(line) - len(line_strip)
+            value_strip = value.replace("(", " ").split()
+            value_strip = value_strip[0] if value_strip else ""
+
+            item = Directive(type=directive,
+                             value=value,
+                             value_strip=value_strip,
+                             line=i,
+                             indent=indent,
+                             members=[])
+
+            tree[indent].append(item)
+            if indent > 0:
+                # get the previous indent, ok this isn't fast but no matter.
+                keys = list(sorted(tree.keys()))
+                key_index = keys.index(indent)
+                parent_indent = keys[key_index - 1]
+                tree[parent_indent][-1].members.append(item)
+    f.close()
+
+    return tree[0]
+
+
+if __name__ == "__main__":
+    # not intended use, but may as well print rst files passed as a test.
+    import sys
+    for arg in sys.argv:
+        if arg.lower().endswith((".txt", ".rst")):
+            items = parse_rst_py(arg)
+            for i in items:
+                print(i)


Property changes on: trunk/blender/source/tests/rst_to_doctree_mini.py
___________________________________________________________________
Added: svn:eol-style
   + native




More information about the Bf-blender-cvs mailing list