extract_docs.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  1. """ This script generates a pandadoc.hpp file representing the Python
  2. wrappers that can be parsed by doxygen to generate the Python documentation.
  3. You need to run this before invoking Doxyfile.python.
  4. It requires a valid makepanda installation with interrogatedb .in
  5. files in the lib/pandac/input directory. """
  6. from __future__ import print_function
  7. __all__ = []
  8. import os
  9. import panda3d
  10. import pandac
  11. from panda3d.interrogatedb import *
  12. if 'interrogate_element_is_sequence' not in globals():
  13. def interrogate_element_is_sequence(element):
  14. return False
  15. if 'interrogate_element_is_mapping' not in globals():
  16. def interrogate_element_is_mapping(element):
  17. return False
  18. LICENSE = """PANDA 3D SOFTWARE
  19. Copyright (c) Carnegie Mellon University. All rights reserved.
  20. All use of this software is subject to the terms of the revised BSD
  21. license. You should have received a copy of this license along
  22. with this source code in a file named \"LICENSE.\"""".split("\n")
  23. MAINPAGE = """@mainpage Panda3D Python API Reference
  24. Welcome to the Panda3D API reference.
  25. Use the links at the top of this page to browse through the list of modules or
  26. the list of classes.
  27. This reference is automatically generated from comments in the source code.
  28. """
  29. def comment(code):
  30. if not code:
  31. return ""
  32. comment = ''
  33. empty_line = False
  34. for line in code.splitlines(False):
  35. line = line.strip('\t\n /')
  36. if line:
  37. if empty_line:
  38. # New paragraph.
  39. comment += '\n\n'
  40. empty_line = False
  41. elif comment:
  42. comment += '\n'
  43. comment += '/// ' + line
  44. else:
  45. empty_line = True
  46. if comment:
  47. return comment
  48. else:
  49. return ''
  50. def block_comment(code):
  51. code = code.strip()
  52. if not code.startswith('///<') and '@verbatim' not in code:
  53. code = code.replace('<', '\\<').replace('>', '\\>')
  54. if not code or code[0] != '/':
  55. # Not really a comment; get rid of it.
  56. return ""
  57. return code
  58. def translateFunctionName(name):
  59. if name.startswith("__"):
  60. return name
  61. new = ""
  62. for i in name.split("_"):
  63. if new == "":
  64. new += i
  65. elif i == "":
  66. pass
  67. elif len(i) == 1:
  68. new += i[0].upper()
  69. else:
  70. new += i[0].upper() + i[1:]
  71. return new
  72. def translateTypeName(name, mangle=True):
  73. # Equivalent to C++ classNameFromCppName
  74. class_name = ""
  75. bad_chars = "!@#$%^&*()<>,.-=+~{}? "
  76. next_cap = False
  77. first_char = mangle
  78. for chr in name:
  79. if (chr == '_' or chr == ' ') and mangle:
  80. next_cap = True
  81. elif chr in bad_chars:
  82. if not mangle:
  83. class_name += '_'
  84. elif next_cap or first_char:
  85. class_name += chr.upper()
  86. next_cap = False
  87. first_char = False
  88. else:
  89. class_name += chr
  90. return class_name
  91. def translated_type_name(type, scoped=True):
  92. while interrogate_type_is_wrapped(type):
  93. if interrogate_type_is_const(type):
  94. return 'const ' + translated_type_name(interrogate_type_wrapped_type(type))
  95. else:
  96. type = interrogate_type_wrapped_type(type)
  97. typename = interrogate_type_name(type)
  98. if typename in ("PyObject", "_object"):
  99. return "object"
  100. elif typename == "PN_stdfloat":
  101. return "float"
  102. elif typename == "size_t":
  103. return "int"
  104. if interrogate_type_is_atomic(type):
  105. token = interrogate_type_atomic_token(type)
  106. if token == 7:
  107. return 'str'
  108. elif token == 8:
  109. return 'long'
  110. elif token == 9:
  111. return 'NoneType'
  112. else:
  113. return typename
  114. if not typename.endswith('_t'):
  115. # Hack: don't mangle size_t etc.
  116. typename = translateTypeName(typename)
  117. if scoped and interrogate_type_is_nested(type):
  118. return translated_type_name(interrogate_type_outer_class(type)) + '::' + typename
  119. else:
  120. return typename
  121. def processElement(handle, element):
  122. if interrogate_element_has_comment(element):
  123. print(comment(interrogate_element_comment(element)), file=handle)
  124. elif interrogate_element_has_getter(element):
  125. # If the property has no comment, use the comment of the getter.
  126. getter = interrogate_element_getter(element)
  127. if interrogate_function_has_comment(getter):
  128. print(block_comment(interrogate_function_comment(getter)), file=handle)
  129. if interrogate_element_is_mapping(element) or \
  130. interrogate_element_is_sequence(element):
  131. suffix = "[]"
  132. else:
  133. suffix = ""
  134. print(translated_type_name(interrogate_element_type(element)), end=' ', file=handle)
  135. print(interrogate_element_name(element) + suffix + ';', file=handle)
  136. def processFunction(handle, function, isConstructor = False):
  137. for i_wrapper in range(interrogate_function_number_of_python_wrappers(function)):
  138. wrapper = interrogate_function_python_wrapper(function, i_wrapper)
  139. if interrogate_wrapper_has_comment(wrapper):
  140. print(block_comment(interrogate_wrapper_comment(wrapper)), file=handle)
  141. if not isConstructor:
  142. if interrogate_function_is_method(function):
  143. if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0):
  144. print("static", end=' ', file=handle)
  145. if interrogate_wrapper_has_return_value(wrapper):
  146. print(translated_type_name(interrogate_wrapper_return_type(wrapper)), end=' ', file=handle)
  147. else:
  148. pass#print >>handle, "void",
  149. print(translateFunctionName(interrogate_function_name(function)) + "(", end=' ', file=handle)
  150. else:
  151. print("__init__(", end=' ', file=handle)
  152. first = True
  153. for i_param in range(interrogate_wrapper_number_of_parameters(wrapper)):
  154. if not interrogate_wrapper_parameter_is_this(wrapper, i_param):
  155. if not first:
  156. print(",", end=' ', file=handle)
  157. print(translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param)), end=' ', file=handle)
  158. if interrogate_wrapper_parameter_has_name(wrapper, i_param):
  159. print(interrogate_wrapper_parameter_name(wrapper, i_param), end=' ', file=handle)
  160. first = False
  161. print(");", file=handle)
  162. def processType(handle, type):
  163. typename = translated_type_name(type, scoped=False)
  164. derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
  165. if interrogate_type_has_comment(type):
  166. print(block_comment(interrogate_type_comment(type)), file=handle)
  167. if interrogate_type_is_enum(type):
  168. print("enum %s {" % typename, file=handle)
  169. for i_value in range(interrogate_type_number_of_enum_values(type)):
  170. docstring = comment(interrogate_type_enum_value_comment(type, i_value))
  171. if docstring:
  172. print(docstring, file=handle)
  173. print(interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ",", file=handle)
  174. elif interrogate_type_is_typedef(type):
  175. wrapped_type = interrogate_type_wrapped_type(type)
  176. if interrogate_type_is_global(wrapped_type):
  177. wrapped_type_name = translated_type_name(wrapped_type)
  178. print("typedef %s %s;" % (wrapped_type_name, typename), file=handle)
  179. return
  180. else:
  181. if interrogate_type_is_struct(type):
  182. classtype = "struct"
  183. elif interrogate_type_is_class(type):
  184. classtype = "class"
  185. elif interrogate_type_is_union(type):
  186. classtype = "union"
  187. else:
  188. print("I don't know what type %s is" % interrogate_type_true_name(type))
  189. return
  190. if len(derivations) > 0:
  191. print("%s %s : public %s {" % (classtype, typename, ", public ".join(derivations)), file=handle)
  192. else:
  193. print("%s %s {" % (classtype, typename), file=handle)
  194. print("public:", file=handle)
  195. for i_ntype in range(interrogate_type_number_of_nested_types(type)):
  196. processType(handle, interrogate_type_get_nested_type(type, i_ntype))
  197. for i_method in range(interrogate_type_number_of_constructors(type)):
  198. processFunction(handle, interrogate_type_get_constructor(type, i_method), True)
  199. for i_method in range(interrogate_type_number_of_methods(type)):
  200. processFunction(handle, interrogate_type_get_method(type, i_method))
  201. for i_method in range(interrogate_type_number_of_make_seqs(type)):
  202. print("list", translateFunctionName(interrogate_make_seq_seq_name(interrogate_type_get_make_seq(type, i_method))), "();", file=handle)
  203. for i_element in range(interrogate_type_number_of_elements(type)):
  204. processElement(handle, interrogate_type_get_element(type, i_element))
  205. print("};", file=handle)
  206. def processModule(handle, package):
  207. print("Processing module %s" % (package))
  208. print("namespace %s {" % package, file=handle)
  209. if package != "core":
  210. print("using namespace core;", file=handle)
  211. for i_type in range(interrogate_number_of_global_types()):
  212. type = interrogate_get_global_type(i_type)
  213. if interrogate_type_has_module_name(type):
  214. module_name = interrogate_type_module_name(type)
  215. if "panda3d." + package == module_name:
  216. processType(handle, type)
  217. else:
  218. typename = interrogate_type_name(type)
  219. print("Type %s has no module name" % typename)
  220. for i_func in range(interrogate_number_of_global_functions()):
  221. func = interrogate_get_global_function(i_func)
  222. if interrogate_function_has_module_name(func):
  223. module_name = interrogate_function_module_name(func)
  224. if "panda3d." + package == module_name:
  225. processFunction(handle, func)
  226. else:
  227. funcname = interrogate_function_name(func)
  228. print("Function %s has no module name" % funcname)
  229. print("}", file=handle)
  230. if __name__ == "__main__":
  231. handle = open("pandadoc.hpp", "w")
  232. mainpage = MAINPAGE.strip()
  233. if mainpage:
  234. print("/**\n * " + mainpage.replace('\n', '\n * ') + '\n */', file=handle)
  235. print(comment("Panda3D modules that are implemented in C++."), file=handle)
  236. print("namespace panda3d {", file=handle)
  237. # Determine the path to the interrogatedb files
  238. pandac = os.path.dirname(pandac.__file__)
  239. interrogate_add_search_directory(os.path.join(pandac, "..", "..", "etc"))
  240. interrogate_add_search_directory(os.path.join(pandac, "input"))
  241. import panda3d.core
  242. processModule(handle, "core")
  243. # Determine the suffix for the extension modules.
  244. import _imp
  245. ext_suffix = _imp.extension_suffixes()[0]
  246. for lib in os.listdir(os.path.dirname(panda3d.__file__)):
  247. if lib.endswith(ext_suffix) and not lib.startswith('core.'):
  248. module_name = lib[:-len(ext_suffix)]
  249. __import__("panda3d." + module_name)
  250. processModule(handle, module_name)
  251. print("}", file=handle)
  252. handle.close()
  253. print("Wrote output to pandadoc.hpp. You can now run:")
  254. print()
  255. print(" doxygen built/direct/directscripts/Doxyfile.python")