tornavis/source/blender/python/doc/sphinx_doc_gen.py

515 lines
16 KiB
Python
Raw Normal View History

# ***** 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,
2010-02-12 14:34:04 +01:00
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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
2010-01-27 23:17:27 +01:00
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))
2010-01-27 23:17:27 +01:00
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")
2010-01-27 23:17:27 +01:00
fw(" :maxdepth: 1\n\n")
fw(" bpy.ops.rst\n\n")
fw(" bpy.types.rst\n\n")
# py modules
2010-01-27 23:17:27 +01:00
fw(" bpy.utils.rst\n\n")
fw(" bpy.app.rst\n\n")
# C modules
2010-01-27 23:17:27 +01:00
fw(" bpy.props.rst\n\n")
2010-01-27 23:17:27 +01:00
fw(" Mathutils.rst\n\n")
fw(" BLF.rst\n\n")
2010-01-27 23:17:27 +01:00
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()
2010-01-27 23:17:27 +01:00
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()
2010-01-27 23:17:27 +01:00
# python modules
from bpy import utils as module
2010-01-27 23:17:27 +01:00
pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities (bpy.utils)")
# C modules
from bpy import app as module
2010-01-27 23:17:27 +01:00
pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data (bpy.app)")
from bpy import props as module
2010-01-27 23:17:27 +01:00
pymodule2sphinx(BASEPATH, "bpy.props", module, "Property Definitions (bpy.props)")
import Mathutils as module
2010-01-27 23:17:27 +01:00
pymodule2sphinx(BASEPATH, "Mathutils", module, "Math Types & Utilities (Mathutils)")
del module
import BLF as module
pymodule2sphinx(BASEPATH, "BLF", module, "Blender Font Drawing (BLF)")
del module
2010-01-27 23:17:27 +01:00
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():
# TODO, rna_info should filter these out!
if "_OT_" in struct.identifier:
continue
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
2010-01-27 23:17:27 +01:00
# 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()