diff options
author | Campbell Barton <ideasman42@gmail.com> | 2010-02-01 00:52:26 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2010-02-01 00:52:26 +0300 |
commit | fd3842f3a2b9f92fabec6268271429200b2713bd (patch) | |
tree | a5c408ce5260de6636b2581abb74a0fcdcd6bf0f /source/blender/python/doc/sphinx_doc_gen.py | |
parent | 02c6d160d7332feaed3efd1a6378406966a25bb1 (diff) |
Mathutils doc improvements + other small things
- bpy.app moved into PyStructSequence (used by sys.float_info)
- added buildinfo into bpy.app.build_*
- bpy.ui removed (wasnt used)
- include external example files in Mathutils docs (only Mathutils and Vector are currently written)
- added support to auto document PyStructSequence's
- CMake had "'s inside all its strings.
Diffstat (limited to 'source/blender/python/doc/sphinx_doc_gen.py')
-rw-r--r-- | source/blender/python/doc/sphinx_doc_gen.py | 508 |
1 files changed, 508 insertions, 0 deletions
diff --git a/source/blender/python/doc/sphinx_doc_gen.py b/source/blender/python/doc/sphinx_doc_gen.py new file mode 100644 index 00000000000..3b92ec28548 --- /dev/null +++ b/source/blender/python/doc/sphinx_doc_gen.py @@ -0,0 +1,508 @@ + # ***** 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + # + # Contributor(s): Campbell Barton + # + # #**** END GPL LICENSE BLOCK #**** + +script_help_msg = ''' +Usage, +run this script from blenders root path once you have compiled blender + ./blender.bin -b -P /b/source/blender/python/doc/sphinx_doc_gen.py + +This will generate python files in "./source/blender/python/doc/sphinx-in" +Generate html docs by running... + + sphinx-build source/blender/python/doc/sphinx-in source/blender/python/doc/sphinx-out + + +For PDF generation + + sphinx-build -b latex source/blender/python/doc/sphinx-in source/blender/python/doc/sphinx-out + cd source/blender/python/doc/sphinx-out + make +''' + + +import os +import inspect +import bpy +import rna_info +reload(rna_info) + +def range_str(val): + if val < -10000000: return '-inf' + if val > 10000000: return 'inf' + if type(val)==float: + return '%g' % val + else: + return str(val) + +def write_indented_lines(ident, fn, text, strip=True): + if text is None: + return + for l in text.split("\n"): + if strip: + fn(ident + l.strip() + "\n") + else: + fn(ident + l + "\n") + + +def pymethod2sphinx(ident, fw, identifier, py_func): + ''' + class method to sphinx + ''' + arg_str = inspect.formatargspec(*inspect.getargspec(py_func)) + if arg_str.startswith("(self, "): + arg_str = "(" + arg_str[7:] + func_type = "method" + elif arg_str.startswith("(cls, "): + arg_str = "(" + arg_str[6:] + func_type = "classmethod" + else: + func_type = "staticmethod" + + fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str)) + if py_func.__doc__: + write_indented_lines(ident + " ", fw, py_func.__doc__) + fw("\n") + + +def pyfunc2sphinx(ident, fw, identifier, py_func, is_class=True): + ''' + function or class method to sphinx + ''' + arg_str = inspect.formatargspec(*inspect.getargspec(py_func)) + + if not is_class: + func_type = "function" + + # ther rest are class methods + elif arg_str.startswith("(self, "): + arg_str = "(" + arg_str[7:] + func_type = "method" + elif arg_str.startswith("(cls, "): + arg_str = "(" + arg_str[6:] + func_type = "classmethod" + else: + func_type = "staticmethod" + + fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str)) + if py_func.__doc__: + write_indented_lines(ident + " ", fw, py_func.__doc__.strip()) + fw("\n") + +def py_c_func2sphinx(ident, fw, identifier, py_func, is_class=True): + ''' + c defined function to sphinx. + ''' + + # dump the docstring, assume its formatted correctly + if py_func.__doc__: + write_indented_lines(ident, fw, py_func.__doc__, False) + fw("\n") + else: + fw(ident + ".. function:: %s()\n\n" % identifier) + fw(ident + " Undocumented function.\n\n" % identifier) + + +def pyprop2sphinx(ident, fw, identifier, py_prop): + ''' + python property to sphinx + ''' + fw(ident + ".. attribute:: %s\n\n" % identifier) + write_indented_lines(ident + " ", fw, py_prop.__doc__) + if py_prop.fset is None: + fw(ident + " (readonly)\n\n") + + +def pymodule2sphinx(BASEPATH, module_name, module, title): + import types + # lame, python wont give some access + MethodDescriptorType = type(dict.get) + GetSetDescriptorType = type(int.real) + + + + filepath = os.path.join(BASEPATH, module_name + ".rst") + + file = open(filepath, "w") + + fw = file.write + + fw(title + "\n") + fw(("=" * len(title)) + "\n\n") + + fw(".. module:: %s\n\n" % module_name) + + if module.__doc__: + # Note, may contain sphinx syntax, dont mangle! + fw(module.__doc__.strip()) + fw("\n\n") + + # write members of the module + # only tested with PyStructs which are not exactly modules + for attribute, descr in sorted(type(module).__dict__.items()): + if type(descr) == types.MemberDescriptorType: + if descr.__doc__: + fw(".. data:: %s\n\n" % attribute) + write_indented_lines(" ", fw, descr.__doc__, False) + fw("\n") + + + classes = [] + + for attribute in dir(module): + if not attribute.startswith("_"): + value = getattr(module, attribute) + + value_type = type(value) + + if value_type == types.FunctionType: + pyfunc2sphinx("", fw, attribute, value, is_class=False) + elif value_type in (types.BuiltinMethodType, types.BuiltinFunctionType): # both the same at the moment but to be future proof + # note: can't get args from these, so dump the string as is + # this means any module used like this must have fully formatted docstrings. + py_c_func2sphinx("", fw, attribute, value, is_class=False) + elif value_type == type: + classes.append((attribute, value)) + # TODO, more types... + + # write collected classes now + for (attribute, value) in classes: + # May need to be its own function + fw(".. class:: %s\n\n" % attribute) + if value.__doc__: + write_indented_lines(" ", fw, value.__doc__, False) + fw("\n") + + for key in sorted(value.__dict__.keys()): + if key.startswith("__"): + continue + descr = value.__dict__[key] + if type(descr) == GetSetDescriptorType: + if descr.__doc__: + fw(" .. attribute:: %s\n\n" % key) + write_indented_lines(" ", fw, descr.__doc__, False) + fw("\n") + + for key in sorted(value.__dict__.keys()): + if key.startswith("__"): + continue + descr = value.__dict__[key] + if type(descr) == MethodDescriptorType: # GetSetDescriptorType, GetSetDescriptorType's are not documented yet + if descr.__doc__: + write_indented_lines(" ", fw, descr.__doc__, False) + fw("\n") + + fw("\n\n") + + file.close() + + + +def rna2sphinx(BASEPATH): + + structs, funcs, ops, props = rna_info.BuildRNAInfo() + + try: + os.mkdir(BASEPATH) + except: + pass + + # conf.py - empty for now + filepath = os.path.join(BASEPATH, "conf.py") + file = open(filepath, "w") + fw = file.write + + + version_string = bpy.app.version_string.split("(")[0] + if bpy.app.build_revision != "Unknown": + version_string = version_string + " r" + bpy.app.build_revision + + fw("project = 'Blender 3D'\n") + # fw("master_doc = 'index'\n") + fw("copyright = u'Blender Foundation'\n") + fw("version = '%s'\n" % version_string) + fw("release = '%s'\n" % version_string) + fw("\n") + # needed for latex, pdf gen + fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n") + fw("latex_paper_size = 'a4paper'\n") + file.close() + + + filepath = os.path.join(BASEPATH, "contents.rst") + file = open(filepath, "w") + fw = file.write + + fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n") + fw(" Blender Documentation contents\n") + fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n") + fw("\n") + fw("This document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date)) + fw("\n") + fw("An introduction to blender and python can be found at <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro>\n") + fw("\n") + fw(".. toctree::\n") + fw(" :maxdepth: 1\n\n") + fw(" bpy.ops.rst\n\n") + fw(" bpy.types.rst\n\n") + + # py modules + fw(" bpy.utils.rst\n\n") + fw(" bpy.app.rst\n\n") + + # C modules + fw(" bpy.props.rst\n\n") + + fw(" Mathutils.rst\n\n") + + file.close() + + + + # internal modules + filepath = os.path.join(BASEPATH, "bpy.ops.rst") + file = open(filepath, "w") + fw = file.write + fw("Blender Operators (bpy.ops)\n") + fw("===========================\n\n") + fw(".. toctree::\n") + fw(" :glob:\n\n") + fw(" bpy.ops.*\n\n") + file.close() + + filepath = os.path.join(BASEPATH, "bpy.types.rst") + file = open(filepath, "w") + fw = file.write + fw("Blender Types (bpy.types)\n") + fw("=========================\n\n") + fw(".. toctree::\n") + fw(" :glob:\n\n") + fw(" bpy.types.*\n\n") + file.close() + + + + # python modules + from bpy import utils as module + pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities (bpy.utils)") + + # C modules + from bpy import app as module + pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data (bpy.app)") + + from bpy import props as module + pymodule2sphinx(BASEPATH, "bpy.props", module, "Property Definitions (bpy.props)") + + import Mathutils as module + pymodule2sphinx(BASEPATH, "Mathutils", module, "Math Types & Utilities (Mathutils)") + del module + + + + if 0: + filepath = os.path.join(BASEPATH, "bpy.rst") + file = open(filepath, "w") + fw = file.write + + fw("\n") + + title = ":mod:`bpy` --- Blender Python Module" + fw("%s\n%s\n\n" % (title, "=" * len(title))) + fw(".. module:: bpy.types\n\n") + file.close() + + def write_param(ident, fw, prop, is_return=False): + if is_return: + id_name = "return" + id_type = "rtype" + kwargs = {"as_ret": True, "class_fmt": ":class:`%s`"} + identifier = "" + else: + id_name = "arg" + id_type = "type" + kwargs = {"as_arg": True, "class_fmt": ":class:`%s`"} + identifier = " %s" % prop.identifier + + type_descr = prop.get_type_description(**kwargs) + if prop.name or prop.description: + fw(ident + ":%s%s: %s\n" % (id_name, identifier, ", ".join([val for val in (prop.name, prop.description) if val]))) + fw(ident + ":%s%s: %s\n" % (id_type, identifier, type_descr)) + + def write_struct(struct): + #if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"): + # return + + #if not struct.identifier == "Object": + # return + + filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % struct.identifier) + file = open(filepath, "w") + fw = file.write + + if struct.base: + title = "%s(%s)" % (struct.identifier, struct.base.identifier) + else: + title = struct.identifier + + fw("%s\n%s\n\n" % (title, "=" * len(title))) + + fw(".. module:: bpy.types\n\n") + + bases = struct.get_bases() + if bases: + if len(bases) > 1: + fw("base classes --- ") + else: + fw("base class --- ") + + fw(", ".join([(":class:`%s`" % base.identifier) for base in reversed(bases)])) + fw("\n\n") + + subclasses = [s for s in structs.values() if s.base is struct] + + if subclasses: + fw("subclasses --- \n") + fw(", ".join([(":class:`%s`" % s.identifier) for s in subclasses])) + fw("\n\n") + + + if struct.base: + fw(".. class:: %s(%s)\n\n" % (struct.identifier, struct.base.identifier)) + else: + fw(".. class:: %s\n\n" % struct.identifier) + + fw(" %s\n\n" % struct.description) + + for prop in struct.properties: + fw(" .. attribute:: %s\n\n" % prop.identifier) + if prop.description: + fw(" %s\n\n" % prop.description) + type_descr = prop.get_type_description(class_fmt=":class:`%s`") + fw(" *type* %s\n\n" % type_descr) + + # python attributes + py_properties = struct.get_py_properties() + py_prop = None + for identifier, py_prop in py_properties: + pyprop2sphinx(" ", fw, identifier, py_prop) + del py_properties, py_prop + + for func in struct.functions: + args_str = ", ".join([prop.get_arg_default(force=False) for prop in func.args]) + + fw(" .. method:: %s(%s)\n\n" % (func.identifier, args_str)) + fw(" %s\n\n" % func.description) + + for prop in func.args: + write_param(" ", fw, prop) + + if len(func.return_values) == 1: + write_param(" ", fw, func.return_values[0], is_return=True) + elif func.return_values: # multiple return values + fw(" :return (%s):\n" % ", ".join([prop.identifier for prop in func.return_values])) + for prop in func.return_values: + type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`") + descr = prop.description + if not descr: + descr = prop.name + fw(" `%s`, %s, %s\n\n" % (prop.identifier, descr, type_descr)) + + fw("\n") + + + # python methods + py_funcs = struct.get_py_functions() + py_func = None + + for identifier, py_func in py_funcs: + pyfunc2sphinx(" ", fw, identifier, py_func, is_class=True) + del py_funcs, py_func + + if struct.references: + # use this otherwise it gets in the index for a normal heading. + fw(".. rubric:: References\n\n") + + for ref in struct.references: + ref_split = ref.split(".") + if len(ref_split) > 2: + ref = ref_split[-2] + "." + ref_split[-1] + fw("* :class:`%s`\n" % ref) + fw("\n") + + + for struct in structs.values(): + write_struct(struct) + + # oeprators + def write_ops(): + fw = None + + last_mod = '' + + for op_key in sorted(ops.keys()): + op = ops[op_key] + + if last_mod != op.module_name: + filepath = os.path.join(BASEPATH, "bpy.ops.%s.rst" % op.module_name) + file = open(filepath, "w") + fw = file.write + + title = "%s Operators" % (op.module_name[0].upper() + op.module_name[1:]) + fw("%s\n%s\n\n" % (title, "=" * len(title))) + + fw(".. module:: bpy.ops.%s\n\n" % op.module_name) + last_mod = op.module_name + + args_str = ", ".join([prop.get_arg_default(force=True) for prop in op.args]) + fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str)) + if op.description: + fw(" %s\n\n" % op.description) + for prop in op.args: + write_param(" ", fw, prop) + if op.args: + fw("\n") + + location = op.get_location() + if location != (None, None): + fw(" *python operator source --- `%s:%d`* \n\n" % location) + + write_ops() + + file.close() + +if __name__ == '__main__': + if 'bpy' not in dir(): + print("\nError, this script must run from inside blender2.5") + print(script_help_msg) + else: + import shutil + + path_in = 'source/blender/python/doc/sphinx-in' + path_out = 'source/blender/python/doc/sphinx-in' + + shutil.rmtree(path_in, True) + shutil.rmtree(path_out, True) + rna2sphinx(path_in) + + # for fast module testing + # os.system("rm source/blender/python/doc/sphinx-in/bpy.types.*.rst") + # os.system("rm source/blender/python/doc/sphinx-in/bpy.ops.*.rst") + + import sys + sys.exit() |