historical/toontown-classic.git/panda/direct/directscripts/extract_docs.py

330 lines
11 KiB
Python
Raw Normal View History

2024-01-16 11:20:27 -06:00
""" This script generates a pandadoc.hpp file representing the Python
wrappers that can be parsed by doxygen to generate the Python documentation.
You need to run this before invoking Doxyfile.python.
It requires a valid makepanda installation with interrogatedb .in
files in the lib/pandac/input directory. """
from __future__ import print_function
__all__ = []
import os, sys
from distutils import sysconfig
import panda3d, pandac
from panda3d.interrogatedb import *
if 'interrogate_element_is_sequence' not in globals():
def interrogate_element_is_sequence(element):
return False
if 'interrogate_element_is_mapping' not in globals():
def interrogate_element_is_mapping(element):
return False
LICENSE = """PANDA 3D SOFTWARE
Copyright (c) Carnegie Mellon University. All rights reserved.
All use of this software is subject to the terms of the revised BSD
license. You should have received a copy of this license along
with this source code in a file named \"LICENSE.\"""".split("\n")
MAINPAGE = """@mainpage Panda3D Python API Reference
Welcome to the Panda3D API reference.
Use the links at the top of this page to browse through the list of modules or
the list of classes.
This reference is automatically generated from comments in the source code.
"""
def comment(code):
if not code:
return ""
comment = ''
empty_line = False
for line in code.splitlines(False):
line = line.strip('\t\n /')
if line:
if empty_line:
# New paragraph.
comment += '\n\n'
empty_line = False
elif comment:
comment += '\n'
comment += '/// ' + line
else:
empty_line = True
if comment:
return comment
else:
return ''
def block_comment(code):
code = code.strip()
if not code.startswith('///<') and '@verbatim' not in code:
code = code.replace('<', '\\<').replace('>', '\\>')
if not code or code[0] != '/':
# Not really a comment; get rid of it.
return ""
return code
def translateFunctionName(name):
if name.startswith("__"):
return name
new = ""
for i in name.split("_"):
if new == "":
new += i
elif i == "":
pass
elif len(i) == 1:
new += i[0].upper()
else:
new += i[0].upper() + i[1:]
return new
def translateTypeName(name, mangle=True):
# Equivalent to C++ classNameFromCppName
class_name = ""
bad_chars = "!@#$%^&*()<>,.-=+~{}? "
next_cap = False
first_char = mangle
for chr in name:
if (chr == '_' or chr == ' ') and mangle:
next_cap = True
elif chr in bad_chars:
if not mangle:
class_name += '_'
elif next_cap or first_char:
class_name += chr.upper()
next_cap = False
first_char = False
else:
class_name += chr
return class_name
def translated_type_name(type, scoped=True):
while interrogate_type_is_wrapped(type):
if interrogate_type_is_const(type):
return 'const ' + translated_type_name(interrogate_type_wrapped_type(type))
else:
type = interrogate_type_wrapped_type(type)
typename = interrogate_type_name(type)
if typename in ("PyObject", "_object"):
return "object"
elif typename == "PN_stdfloat":
return "float"
elif typename == "size_t":
return "int"
if interrogate_type_is_atomic(type):
token = interrogate_type_atomic_token(type)
if token == 7:
return 'str'
elif token == 8:
return 'long'
elif token == 9:
return 'NoneType'
else:
return typename
if not typename.endswith('_t'):
# Hack: don't mangle size_t etc.
typename = translateTypeName(typename)
if scoped and interrogate_type_is_nested(type):
return translated_type_name(interrogate_type_outer_class(type)) + '::' + typename
else:
return typename
def processElement(handle, element):
if interrogate_element_has_comment(element):
print(comment(interrogate_element_comment(element)), file=handle)
elif interrogate_element_has_getter(element):
# If the property has no comment, use the comment of the getter.
getter = interrogate_element_getter(element)
if interrogate_function_has_comment(getter):
print(block_comment(interrogate_function_comment(getter)), file=handle)
if interrogate_element_is_mapping(element) or \
interrogate_element_is_sequence(element):
suffix = "[]"
else:
suffix = ""
print(translated_type_name(interrogate_element_type(element)), end=' ', file=handle)
print(interrogate_element_name(element) + suffix + ';', file=handle)
def processFunction(handle, function, isConstructor = False):
for i_wrapper in range(interrogate_function_number_of_python_wrappers(function)):
wrapper = interrogate_function_python_wrapper(function, i_wrapper)
if interrogate_wrapper_has_comment(wrapper):
print(block_comment(interrogate_wrapper_comment(wrapper)), file=handle)
if not isConstructor:
if interrogate_function_is_method(function):
if not interrogate_wrapper_number_of_parameters(wrapper) > 0 or not interrogate_wrapper_parameter_is_this(wrapper, 0):
print("static", end=' ', file=handle)
if interrogate_wrapper_has_return_value(wrapper):
print(translated_type_name(interrogate_wrapper_return_type(wrapper)), end=' ', file=handle)
else:
pass#print >>handle, "void",
print(translateFunctionName(interrogate_function_name(function)) + "(", end=' ', file=handle)
else:
print("__init__(", end=' ', file=handle)
first = True
for i_param in range(interrogate_wrapper_number_of_parameters(wrapper)):
if not interrogate_wrapper_parameter_is_this(wrapper, i_param):
if not first:
print(",", end=' ', file=handle)
print(translated_type_name(interrogate_wrapper_parameter_type(wrapper, i_param)), end=' ', file=handle)
if interrogate_wrapper_parameter_has_name(wrapper, i_param):
print(interrogate_wrapper_parameter_name(wrapper, i_param), end=' ', file=handle)
first = False
print(");", file=handle)
def processType(handle, type):
typename = translated_type_name(type, scoped=False)
derivations = [ translated_type_name(interrogate_type_get_derivation(type, n)) for n in range(interrogate_type_number_of_derivations(type)) ]
if interrogate_type_has_comment(type):
print(block_comment(interrogate_type_comment(type)), file=handle)
if interrogate_type_is_enum(type):
print("enum %s {" % typename, file=handle)
for i_value in range(interrogate_type_number_of_enum_values(type)):
docstring = comment(interrogate_type_enum_value_comment(type, i_value))
if docstring:
print(docstring, file=handle)
print(interrogate_type_enum_value_name(type, i_value), "=", interrogate_type_enum_value(type, i_value), ",", file=handle)
elif interrogate_type_is_typedef(type):
wrapped_type = interrogate_type_wrapped_type(type)
if interrogate_type_is_global(wrapped_type):
wrapped_type_name = translated_type_name(wrapped_type)
print("typedef %s %s;" % (wrapped_type_name, typename), file=handle)
return
else:
if interrogate_type_is_struct(type):
classtype = "struct"
elif interrogate_type_is_class(type):
classtype = "class"
elif interrogate_type_is_union(type):
classtype = "union"
else:
print("I don't know what type %s is" % interrogate_type_true_name(type))
return
if len(derivations) > 0:
print("%s %s : public %s {" % (classtype, typename, ", public ".join(derivations)), file=handle)
else:
print("%s %s {" % (classtype, typename), file=handle)
print("public:", file=handle)
for i_ntype in range(interrogate_type_number_of_nested_types(type)):
processType(handle, interrogate_type_get_nested_type(type, i_ntype))
for i_method in range(interrogate_type_number_of_constructors(type)):
processFunction(handle, interrogate_type_get_constructor(type, i_method), True)
for i_method in range(interrogate_type_number_of_methods(type)):
processFunction(handle, interrogate_type_get_method(type, i_method))
for i_method in range(interrogate_type_number_of_make_seqs(type)):
print("list", translateFunctionName(interrogate_make_seq_seq_name(interrogate_type_get_make_seq(type, i_method))), "();", file=handle)
for i_element in range(interrogate_type_number_of_elements(type)):
processElement(handle, interrogate_type_get_element(type, i_element))
print("};", file=handle)
def processModule(handle, package):
print("Processing module %s" % (package))
print("namespace %s {" % package, file=handle)
if package != "core":
print("using namespace core;", file=handle)
for i_type in range(interrogate_number_of_global_types()):
type = interrogate_get_global_type(i_type)
if interrogate_type_has_module_name(type):
module_name = interrogate_type_module_name(type)
if "panda3d." + package == module_name:
processType(handle, type)
else:
print("Type %s has no module name" % typename)
for i_func in range(interrogate_number_of_global_functions()):
func = interrogate_get_global_function(i_func)
if interrogate_function_has_module_name(func):
module_name = interrogate_function_module_name(func)
if "panda3d." + package == module_name:
processFunction(handle, func)
else:
print("Type %s has no module name" % typename)
print("}", file=handle)
if __name__ == "__main__":
handle = open("pandadoc.hpp", "w")
mainpage = MAINPAGE.strip()
if mainpage:
print("/**\n * " + mainpage.replace('\n', '\n * ') + '\n */', file=handle)
print(comment("Panda3D modules that are implemented in C++."), file=handle)
print("namespace panda3d {", file=handle)
# Determine the path to the interrogatedb files
pandac = os.path.dirname(pandac.__file__)
interrogate_add_search_directory(os.path.join(pandac, "..", "..", "etc"))
interrogate_add_search_directory(os.path.join(pandac, "input"))
import panda3d.core
processModule(handle, "core")
# Determine the suffix for the extension modules.
if sys.version_info >= (3, 0):
import _imp
ext_suffix = _imp.extension_suffixes()[0]
elif sys.platform == "win32":
ext_suffix = ".pyd"
else:
ext_suffix = ".so"
for lib in os.listdir(os.path.dirname(panda3d.__file__)):
if lib.endswith(ext_suffix) and not lib.startswith('core.'):
module_name = lib[:-len(ext_suffix)]
__import__("panda3d." + module_name)
processModule(handle, module_name)
print("}", file=handle)
handle.close()
print("Wrote output to pandadoc.hpp. You can now run:")
print()
print(" doxygen built/direct/directscripts/Doxyfile.python")