extract_docs.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  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. __all__ = []
  7. import os
  8. import panda3d, pandac
  9. from panda3d.dtoolconfig import *
  10. LICENSE = """PANDA 3D SOFTWARE
  11. Copyright (c) Carnegie Mellon University. All rights reserved.
  12. All use of this software is subject to the terms of the revised BSD
  13. license. You should have received a copy of this license along
  14. with this source code in a file named \"LICENSE.\"""".split("\n")
  15. def comment(code):
  16. if not code:
  17. return ""
  18. comment = ''
  19. empty_line = False
  20. for line in code.splitlines(False):
  21. line = line.strip('\t\n /')
  22. if line:
  23. if empty_line:
  24. # New paragraph.
  25. comment += '\n\n'
  26. empty_line = False
  27. elif comment:
  28. comment += '\n'
  29. comment += '/// ' + line
  30. else:
  31. empty_line = True
  32. if comment:
  33. return comment
  34. else:
  35. return ''
  36. def block_comment(code):
  37. if not code:
  38. return ""
  39. lines = code.split("\n")
  40. newlines = []
  41. indent = 0
  42. reading_desc = False
  43. for line in lines:
  44. if line.startswith("////"):
  45. continue
  46. line = line.rstrip()
  47. strline = line.lstrip('/ \t')
  48. if ':' in strline:
  49. pre, post = strline.split(':', 1)
  50. pre = pre.rstrip()
  51. if pre == "Description":
  52. strline = post.lstrip()
  53. elif pre in ("Class", "Access", "Function", "Created by", "Enum"):
  54. continue
  55. if strline or len(newlines) > 0:
  56. newlines.append('/// ' + strline)
  57. #if reading_desc:
  58. # newlines.append('/// ' + line[min(indent, len(line) - len(strline)):])
  59. #else:
  60. # # A "Description:" text starts the description.
  61. # if strline.startswith("Description"):
  62. # strline = strline[11:].lstrip(': \t')
  63. # indent = len(line) - len(strline)
  64. # reading_desc = True
  65. # newlines.append('/// ' + strline)
  66. # else:
  67. # print line
  68. newcode = '\n'.join(newlines)
  69. if len(newcode) > 0:
  70. return newcode
  71. else:
  72. return ""
  73. def translateFunctionName(name):
  74. if name.startswith("__"):
  75. return name
  76. new = ""
  77. for i in name.split("_"):
  78. if new == "":
  79. new += i
  80. elif i == "":
  81. pass
  82. elif len(i) == 1:
  83. new += i[0].upper()
  84. else:
  85. new += i[0].upper() + i[1:]
  86. return new
  87. def translateTypeName(name, mangle=True):
  88. # Equivalent to C++ classNameFromCppName
  89. class_name = ""
  90. bad_chars = "!@#$%^&*()<>,.-=+~{}? "
  91. next_cap = False
  92. first_char = mangle
  93. for chr in name:
  94. if (chr == '_' or chr == ' ') and mangle:
  95. next_cap = True
  96. elif chr in bad_chars:
  97. if not mangle:
  98. class_name += '_'
  99. elif next_cap or first_char:
  100. class_name += chr.upper()
  101. next_cap = False
  102. first_char = False
  103. else:
  104. class_name += chr
  105. return class_name
  106. def translated_type_name(type, scoped=True):
  107. while interrogate_type_is_wrapped(type):
  108. if interrogate_type_is_const(type):
  109. return 'const ' + translated_type_name(interrogate_type_wrapped_type(type))
  110. else:
  111. type = interrogate_type_wrapped_type(type)
  112. typename = interrogate_type_name(type)
  113. if typename in ("PyObject", "_object"):
  114. return "object"
  115. elif typename == "PN_stdfloat":
  116. return "float"
  117. if interrogate_type_is_atomic(type):
  118. token = interrogate_type_atomic_token(type)
  119. if token == 7:
  120. return 'str'
  121. else:
  122. return typename
  123. if not typename.endswith('_t'):
  124. # Hack: don't mangle size_t etc.
  125. typename = translateTypeName(typename)
  126. if scoped and interrogate_type_is_nested(type):
  127. return translated_type_name(interrogate_type_outer_class(type)) + '::' + typename
  128. else:
  129. return typename
  130. def processElement(handle, element):
  131. if interrogate_element_has_comment(element):
  132. print >>handle, comment(interrogate_element_comment(element))
  133. print >>handle, translated_type_name(interrogate_element_type(element)),
  134. print >>handle, interrogate_element_name(element) + ';'
  135. def processFunction(handle, function, isConstructor = False):
  136. for i_wrapper in xrange(interrogate_function_number_of_python_wrappers(function)):
  137. wrapper = interrogate_function_python_wrapper(function, i_wrapper)
  138. if interrogate_wrapper_has_comment(wrapper):
  139. print >>handle, block_comment(interrogate_wrapper_comment(wrapper))
  140. if not isConstructor:
  141. if interrogate_function_is_method(function):
  142. if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0):
  143. print >>handle, "static",
  144. if interrogate_wrapper_has_return_value(wrapper):
  145. print >>handle, translated_type_name(interrogate_wrapper_return_type(wrapper)),
  146. else:
  147. pass#print >>handle, "void",
  148. print >>handle, translateFunctionName(interrogate_function_name(function)) + "(",
  149. else:
  150. print >>handle, "__init__(",
  151. first = True
  152. for i_param in range(interrogate_wrapper_number_of_parameters(wrapper)):
  153. if not interrogate_wrapper_parameter_is_this(wrapper, i_param):
  154. if not first:
  155. print >>handle, ",",
  156. print >>handle, translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param)),
  157. if interrogate_wrapper_parameter_has_name(wrapper, i_param):
  158. print >>handle, interrogate_wrapper_parameter_name(wrapper, i_param),
  159. first = False
  160. print >>handle, ");"
  161. def processType(handle, type):
  162. typename = translated_type_name(type, scoped=False)
  163. derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
  164. if interrogate_type_has_comment(type):
  165. print >>handle, block_comment(interrogate_type_comment(type))
  166. if interrogate_type_is_enum(type):
  167. print >>handle, "enum %s {" % typename
  168. for i_value in range(interrogate_type_number_of_enum_values(type)):
  169. docstring = comment(interrogate_type_enum_value_comment(type, i_value))
  170. if docstring:
  171. print >>handle, docstring
  172. print >>handle, interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ","
  173. elif interrogate_type_is_typedef(type):
  174. wrapped_type = translated_type_name(interrogate_type_wrapped_type(type))
  175. print >>handle, "typedef %s %s;" % (wrapped_type, typename)
  176. return
  177. else:
  178. if interrogate_type_is_struct(type):
  179. classtype = "struct"
  180. elif interrogate_type_is_class(type):
  181. classtype = "class"
  182. elif interrogate_type_is_union(type):
  183. classtype = "union"
  184. else:
  185. print "I don't know what type %s is" % interrogate_type_true_name(type)
  186. return
  187. if len(derivations) > 0:
  188. print >>handle, "%s %s : public %s {" % (classtype, typename, ", public ".join(derivations))
  189. else:
  190. print >>handle, "%s %s {" % (classtype, typename)
  191. print >>handle, "public:"
  192. for i_ntype in xrange(interrogate_type_number_of_nested_types(type)):
  193. processType(handle, interrogate_type_get_nested_type(type, i_ntype))
  194. for i_method in xrange(interrogate_type_number_of_constructors(type)):
  195. processFunction(handle, interrogate_type_get_constructor(type, i_method), True)
  196. for i_method in xrange(interrogate_type_number_of_methods(type)):
  197. processFunction(handle, interrogate_type_get_method(type, i_method))
  198. for i_method in xrange(interrogate_type_number_of_make_seqs(type)):
  199. print >>handle, "list", translateFunctionName(interrogate_make_seq_seq_name(interrogate_type_get_make_seq(type, i_method))), "();"
  200. for i_element in xrange(interrogate_type_number_of_elements(type)):
  201. processElement(handle, interrogate_type_get_element(type, i_element))
  202. print >>handle, "};"
  203. def processModule(handle, package):
  204. print >>handle, "namespace %s {" % package
  205. if package != "core":
  206. print >>handle, "using namespace core;"
  207. for i_type in xrange(interrogate_number_of_global_types()):
  208. type = interrogate_get_global_type(i_type)
  209. if interrogate_type_has_module_name(type):
  210. module_name = interrogate_type_module_name(type)
  211. if "panda3d." + package == module_name:
  212. processType(handle, type)
  213. else:
  214. print "Type %s has no module name" % typename
  215. for i_func in xrange(interrogate_number_of_global_functions()):
  216. func = interrogate_get_global_function(i_func)
  217. if interrogate_function_has_module_name(func):
  218. module_name = interrogate_function_module_name(func)
  219. if "panda3d." + package == module_name:
  220. processFunction(handle, func)
  221. else:
  222. print "Type %s has no module name" % typename
  223. print >>handle, "}"
  224. if __name__ == "__main__":
  225. handle = open("pandadoc.hpp", "w")
  226. print >>handle, comment("Panda3D modules that are implemented in C++.")
  227. print >>handle, "namespace panda3d {"
  228. # Determine the path to the interrogatedb files
  229. interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "..", "..", "etc"))
  230. interrogate_add_search_directory(os.path.join(os.path.dirname(pandac.__file__), "input"))
  231. import panda3d.core
  232. processModule(handle, "core")
  233. for lib in os.listdir(os.path.dirname(panda3d.__file__)):
  234. if lib.endswith(('.pyd', '.so')) and not lib.startswith('core.'):
  235. module_name = os.path.splitext(lib)[0]
  236. __import__("panda3d." + module_name)
  237. processModule(handle, module_name)
  238. print >>handle, "}"
  239. handle.close()