bl_rst_completeness.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  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. # <pep8 compliant>
  19. # run this script in the game engine.
  20. # or on the command line with...
  21. # ./blender.bin --background -noaudio --python tests/python/bl_rst_completeness.py
  22. '''
  23. filepath = "/src/blender/tests/python/bl_rst_completeness.py"
  24. exec(compile(open(filepath).read(), filepath, 'exec'))
  25. '''
  26. import os
  27. THIS_DIR = os.path.dirname(__file__)
  28. RST_DIR = os.path.normpath(os.path.join(THIS_DIR, "..", "..", "doc", "python_api", "rst"))
  29. import sys
  30. sys.path.append(THIS_DIR)
  31. import rst_to_doctree_mini
  32. # (file, module)
  33. modules = (
  34. ("bgl.rst", "bgl", True),
  35. ("gpu.rst", "gpu", False),
  36. )
  37. def is_directive_pydata(filepath, directive):
  38. if directive.type in {"function", "method", "class", "attribute", "data"}:
  39. return True
  40. elif directive.type in {"module", "note", "warning", "code-block", "hlist", "seealso"}:
  41. return False
  42. elif directive.type == "literalinclude": # TODO
  43. return False
  44. else:
  45. print(directive_to_str(filepath, directive), end=" ")
  46. print("unknown directive type %r" % directive.type)
  47. return False
  48. def directive_to_str(filepath, directive):
  49. return "%s:%d:%d:" % (filepath, directive.line + 1, directive.indent)
  50. def directive_members_dict(filepath, directive_members):
  51. return {directive.value_strip: directive for directive in directive_members
  52. if is_directive_pydata(filepath, directive)}
  53. def module_validate(filepath, mod, mod_name, doctree, partial_ok):
  54. # RST member missing from MODULE ???
  55. for directive in doctree:
  56. # print(directive.type)
  57. if is_directive_pydata(filepath, directive):
  58. attr = directive.value_strip
  59. has_attr = hasattr(mod, attr)
  60. ok = False
  61. if not has_attr:
  62. # so we can have glNormal docs cover glNormal3f
  63. if partial_ok:
  64. for s in dir(mod):
  65. if s.startswith(attr):
  66. ok = True
  67. break
  68. if not ok:
  69. print(directive_to_str(filepath, directive), end=" ")
  70. print("rst contains non existing member %r" % attr)
  71. # if its a class, scan down the class...
  72. # print(directive.type)
  73. if has_attr:
  74. if directive.type == "class":
  75. cls = getattr(mod, attr)
  76. # print("directive: ", directive)
  77. for directive_child in directive.members:
  78. # print("directive_child: ", directive_child)
  79. if is_directive_pydata(filepath, directive_child):
  80. attr_child = directive_child.value_strip
  81. if attr_child not in cls.__dict__:
  82. attr_id = "%s.%s" % (attr, attr_child)
  83. print(directive_to_str(filepath, directive_child), end=" ")
  84. print("rst contains non existing class member %r" % attr_id)
  85. # MODULE member missing from RST ???
  86. doctree_dict = directive_members_dict(filepath, doctree)
  87. for attr in dir(mod):
  88. if attr.startswith("_"):
  89. continue
  90. directive = doctree_dict.get(attr)
  91. if directive is None:
  92. print("module contains undocumented member %r from %r" % ("%s.%s" % (mod_name, attr), filepath))
  93. else:
  94. if directive.type == "class":
  95. directive_dict = directive_members_dict(filepath, directive.members)
  96. cls = getattr(mod, attr)
  97. for attr_child in cls.__dict__.keys():
  98. if attr_child.startswith("_"):
  99. continue
  100. if attr_child not in directive_dict:
  101. attr_id = "%s.%s.%s" % (mod_name, attr, attr_child), filepath
  102. print("module contains undocumented member %r from %r" % attr_id)
  103. def main():
  104. for filename, modname, partial_ok in modules:
  105. filepath = os.path.join(RST_DIR, filename)
  106. if not os.path.exists(filepath):
  107. raise Exception("%r not found" % filepath)
  108. doctree = rst_to_doctree_mini.parse_rst_py(filepath)
  109. __import__(modname)
  110. mod = sys.modules[modname]
  111. module_validate(filepath, mod, modname, doctree, partial_ok)
  112. if __name__ == "__main__":
  113. main()