initial sphinx doc generation support for python and C modules.

python modules bpy.app, bpy.utils are now included in docs.
C defined python module bpy.props has its docstrings extracted and written directly into sphinx docs since the C methods cant be inspected.

added docstrings to bpy.props and improved some in bpy.utils.

will update online docs tomorrow.
This commit is contained in:
Campbell Barton 2010-01-22 02:04:25 +00:00
parent 68874c9aa4
commit 7f1a8a947d
4 changed files with 234 additions and 48 deletions

@ -18,6 +18,27 @@
# <pep8 compliant> # <pep8 compliant>
"""
This module contains application values that remain unchanged during runtime.
.. data:: version
The Blender version as a tuple of 3 numbers. eg. (2, 50, 11)
.. data:: version_string
The Blender version formatted as a string.
.. data:: home
The blender home directory, normally matching $HOME
.. data:: binary_path
The location of blenders executable, useful for utilities that spawn new instances.
"""
# constants # constants
import _bpy import _bpy
version = _bpy._VERSION version = _bpy._VERSION

@ -18,13 +18,18 @@
# <pep8 compliant> # <pep8 compliant>
import bpy """
import os This module contains utility functions spesific to blender but
not assosiated with blenders internal data.
"""
import bpy as _bpy
import os as _os
def expandpath(path): def expandpath(path):
if path.startswith("//"): if path.startswith("//"):
return os.path.join(os.path.dirname(bpy.data.filename), path[2:]) return _os.path.join(_os.path.dirname(_bpy.data.filename), path[2:])
return path return path
@ -47,21 +52,23 @@ _unclean_chars = ''.join([chr(i) for i in _unclean_chars])
def clean_name(name, replace="_"): def clean_name(name, replace="_"):
''' """
Returns a name with characters replaced that may cause problems under various circumstances, such as writing to a file.
All characters besides A-Z/a-z, 0-9 are replaced with "_" All characters besides A-Z/a-z, 0-9 are replaced with "_"
or the replace argumet if defined. or the replace argumet if defined.
''' """
for ch in _unclean_chars: for ch in _unclean_chars:
name = name.replace(ch, replace) name = name.replace(ch, replace)
return name return name
def display_name(name): def display_name(name):
''' """
Only capitalize all lowercase names, mixed case use them as is. Creates a display string from name to be used menus and the user interface.
should work with filenames and module names. Capitalize the first letter in all lowercase names, mixed case names are kept as is.
''' Intended for use with filenames and module names.
name_base = os.path.splitext(name)[0] """
name_base = _os.path.splitext(name)[0]
# string replacements # string replacements
name_base = name_base.replace("_colon_", ":") name_base = name_base.replace("_colon_", ":")
@ -75,39 +82,44 @@ def display_name(name):
# base scripts # base scripts
_scripts = os.path.join(os.path.dirname(__file__), os.path.pardir, os.path.pardir) _scripts = _os.path.join(_os.path.dirname(__file__), _os.path.pardir, _os.path.pardir)
_scripts = (os.path.normpath(_scripts), ) _scripts = (_os.path.normpath(_scripts), )
def script_paths(*args): def script_paths(*args):
"""
Returns a list of valid script paths from the home directory and user preferences.
Accepts any number of string arguments which are joined to make a path.
"""
scripts = list(_scripts) scripts = list(_scripts)
# add user scripts dir # add user scripts dir
user_script_path = bpy.context.user_preferences.filepaths.python_scripts_directory user_script_path = _bpy.context.user_preferences.filepaths.python_scripts_directory
if not user_script_path: if not user_script_path:
# XXX - WIN32 needs checking, perhaps better call a blender internal function. # XXX - WIN32 needs checking, perhaps better call a blender internal function.
user_script_path = os.path.join(os.path.expanduser("~"), ".blender", "scripts") user_script_path = _os.path.join(_os.path.expanduser("~"), ".blender", "scripts")
user_script_path = os.path.normpath(user_script_path) user_script_path = _os.path.normpath(user_script_path)
if user_script_path not in scripts and os.path.isdir(user_script_path): if user_script_path not in scripts and _os.path.isdir(user_script_path):
scripts.append(user_script_path) scripts.append(user_script_path)
if not args: if not args:
return scripts return scripts
subdir = os.path.join(*args) subdir = _os.path.join(*args)
script_paths = [] script_paths = []
for path in scripts: for path in scripts:
path_subdir = os.path.join(path, subdir) path_subdir = _os.path.join(path, subdir)
if os.path.isdir(path_subdir): if _os.path.isdir(path_subdir):
script_paths.append(path_subdir) script_paths.append(path_subdir)
return script_paths return script_paths
_presets = os.path.join(_scripts[0], "presets") # FIXME - multiple paths _presets = _os.path.join(_scripts[0], "presets") # FIXME - multiple paths
def preset_paths(subdir): def preset_paths(subdir):
@ -115,4 +127,4 @@ def preset_paths(subdir):
Returns a list of paths for a spesific preset. Returns a list of paths for a spesific preset.
''' '''
return (os.path.join(_presets, subdir), ) return (_os.path.join(_presets, subdir), )

@ -46,6 +46,10 @@ static PyObject *bpy_prop_deferred_return(void *func, PyObject *kw)
/* Function that sets RNA, NOTE - self is NULL when called from python, but being abused from C so we can pass the srna allong /* Function that sets RNA, NOTE - self is NULL when called from python, but being abused from C so we can pass the srna allong
* This isnt incorrect since its a python object - but be careful */ * This isnt incorrect since its a python object - but be careful */
static char BPy_BoolProperty_doc[] =
".. function:: BoolProperty(name=\"\", description=\"\", default=False, hidden=False)\n"
"\n"
" Returns a new boolean property definition..";
PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
@ -79,6 +83,10 @@ PyObject *BPy_BoolProperty(PyObject *self, PyObject *args, PyObject *kw)
} }
} }
static char BPy_IntProperty_doc[] =
".. function:: IntProperty(name=\"\", description=\"\", default=0, min=-sys.maxint, max=sys.maxint, soft_min=-sys.maxint, soft_max=sys.maxint, step=1, hidden=False)\n"
"\n"
" Returns a new int property definition.";
PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -113,6 +121,10 @@ PyObject *BPy_IntProperty(PyObject *self, PyObject *args, PyObject *kw)
} }
} }
static char BPy_FloatProperty_doc[] =
".. function:: FloatProperty(name=\"\", description=\"\", default=0.0, min=sys.float_info.min, max=sys.float_info.max, soft_min=sys.float_info.min, soft_max=sys.float_info.max, step=3, precision=2, hidden=False)\n"
"\n"
" Returns a new float property definition.";
PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -147,6 +159,10 @@ PyObject *BPy_FloatProperty(PyObject *self, PyObject *args, PyObject *kw)
} }
} }
static char BPy_FloatVectorProperty_doc[] =
".. function:: FloatVectorProperty(name=\"\", description=\"\", default=(0.0, 0.0, 0.0), min=sys.float_info.min, max=sys.float_info.max, soft_min=sys.float_info.min, soft_max=sys.float_info.max, step=3, precision=2, hidden=False, size=3)\n"
"\n"
" Returns a new vector float property definition.";
PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -213,6 +229,10 @@ PyObject *BPy_FloatVectorProperty(PyObject *self, PyObject *args, PyObject *kw)
} }
} }
static char BPy_StringProperty_doc[] =
".. function:: StringProperty(name=\"\", description=\"\", default=\"\", maxlen=0, hidden=False)\n"
"\n"
" Returns a new string property definition.";
PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_StringProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -291,6 +311,13 @@ static EnumPropertyItem *enum_items_from_py(PyObject *value, const char *def, in
return items; return items;
} }
static char BPy_EnumProperty_doc[] =
".. function:: EnumProperty(items, name=\"\", description=\"\", default=\"\", hidden=False)\n"
"\n"
" Returns a new enumerator property definition.\n"
"\n"
" :arg items: The items that make up this enumerator.\n"
" :type items: sequence of string triplets";
PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_EnumProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -349,6 +376,13 @@ static StructRNA *pointer_type_from_py(PyObject *value)
return srna; return srna;
} }
static char BPy_PointerProperty_doc[] =
".. function:: PointerProperty(items, type=\"\", description=\"\", default=\"\", hidden=False)\n"
"\n"
" Returns a new pointer property definition.\n"
"\n"
" :arg type: Dynamic type from :mod:`bpy.types`.\n"
" :type type: class";
PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -388,6 +422,13 @@ PyObject *BPy_PointerProperty(PyObject *self, PyObject *args, PyObject *kw)
return NULL; return NULL;
} }
static char BPy_CollectionProperty_doc[] =
".. function:: CollectionProperty(items, type=\"\", description=\"\", default=\"\", hidden=False)\n"
"\n"
" Returns a new collection property definition.\n"
"\n"
" :arg type: Dynamic type from :mod:`bpy.types`.\n"
" :type type: class";
PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw) PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
{ {
StructRNA *srna; StructRNA *srna;
@ -428,21 +469,22 @@ PyObject *BPy_CollectionProperty(PyObject *self, PyObject *args, PyObject *kw)
} }
static struct PyMethodDef props_methods[] = { static struct PyMethodDef props_methods[] = {
{"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"BoolProperty", (PyCFunction)BPy_BoolProperty, METH_VARARGS|METH_KEYWORDS, BPy_BoolProperty_doc},
{"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"IntProperty", (PyCFunction)BPy_IntProperty, METH_VARARGS|METH_KEYWORDS, BPy_IntProperty_doc},
{"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"FloatProperty", (PyCFunction)BPy_FloatProperty, METH_VARARGS|METH_KEYWORDS, BPy_FloatProperty_doc},
{"FloatVectorProperty", (PyCFunction)BPy_FloatVectorProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"FloatVectorProperty", (PyCFunction)BPy_FloatVectorProperty, METH_VARARGS|METH_KEYWORDS, BPy_FloatVectorProperty_doc},
{"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"StringProperty", (PyCFunction)BPy_StringProperty, METH_VARARGS|METH_KEYWORDS, BPy_StringProperty_doc},
{"EnumProperty", (PyCFunction)BPy_EnumProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"EnumProperty", (PyCFunction)BPy_EnumProperty, METH_VARARGS|METH_KEYWORDS, BPy_EnumProperty_doc},
{"PointerProperty", (PyCFunction)BPy_PointerProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"PointerProperty", (PyCFunction)BPy_PointerProperty, METH_VARARGS|METH_KEYWORDS, BPy_PointerProperty_doc},
{"CollectionProperty", (PyCFunction)BPy_CollectionProperty, METH_VARARGS|METH_KEYWORDS, ""}, {"CollectionProperty", (PyCFunction)BPy_CollectionProperty, METH_VARARGS|METH_KEYWORDS, BPy_CollectionProperty_doc},
{NULL, NULL, 0, NULL} {NULL, NULL, 0, NULL}
}; };
static struct PyModuleDef props_module = { static struct PyModuleDef props_module = {
PyModuleDef_HEAD_INIT, PyModuleDef_HEAD_INIT,
"bpy.props", "bpy.props",
"", "This module defines properties to extend blenders internal data, the result of these functions"
" is used to assign properties to classes registered with blender and can't be used directly.",
-1,/* multiple "initialization" just copies the module dict. */ -1,/* multiple "initialization" just copies the module dict. */
props_methods, props_methods,
NULL, NULL, NULL, NULL NULL, NULL, NULL, NULL

@ -49,6 +49,114 @@ def write_indented_lines(ident, fn, text):
for l in text.split("\n"): for l in text.split("\n"):
fn(ident + l.strip() + "\n") fn(ident + l.strip() + "\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__:
for l in py_func.__doc__.split("\n"):
fw(ident + l + "\n")
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
filepath = os.path.join(BASEPATH, module_name + ".rst")
file = open(filepath, "w")
print(filepath)
print(filepath)
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")
for attribute in dir(module):
if not attribute.startswith("_"):
value = getattr(module, attribute)
value_type = type(value)
print(attribute, value_type)
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)
# TODO, more types...
file.close()
def rna2sphinx(BASEPATH): def rna2sphinx(BASEPATH):
structs, funcs, ops, props = rna_info.BuildRNAInfo() structs, funcs, ops, props = rna_info.BuildRNAInfo()
@ -80,8 +188,27 @@ def rna2sphinx(BASEPATH):
fw(" :glob:\n\n") fw(" :glob:\n\n")
fw(" bpy.ops.*\n\n") fw(" bpy.ops.*\n\n")
fw(" bpy.types.*\n\n") fw(" bpy.types.*\n\n")
# py modules
fw(" bpy.utils\n\n")
fw(" bpy.app\n\n")
# C modules
fw(" bpy.props\n\n")
file.close() file.close()
# python modules
from bpy import utils as module
pymodule2sphinx(BASEPATH, "bpy.utils", module, "Blender Python Utilities")
from bpy import app as module
pymodule2sphinx(BASEPATH, "bpy.app", module, "Blender Python Application Constants")
from bpy import props as module
pymodule2sphinx(BASEPATH, "bpy.props", module, "Blender Python Property Definitions")
del module
if 0: if 0:
filepath = os.path.join(BASEPATH, "bpy.rst") filepath = os.path.join(BASEPATH, "bpy.rst")
file = open(filepath, "w") file = open(filepath, "w")
@ -167,10 +294,7 @@ def rna2sphinx(BASEPATH):
py_properties = struct.get_py_properties() py_properties = struct.get_py_properties()
py_prop = None py_prop = None
for identifier, py_prop in py_properties: for identifier, py_prop in py_properties:
fw(" .. attribute:: %s\n\n" % identifier) pyprop2sphinx(" ", fw, identifier, py_prop)
write_indented_lines(" ", fw, py_prop.__doc__)
if py_prop.fset is None:
fw(" (readonly)\n\n")
del py_properties, py_prop del py_properties, py_prop
for func in struct.functions: for func in struct.functions:
@ -201,20 +325,7 @@ def rna2sphinx(BASEPATH):
py_func = None py_func = None
for identifier, py_func in py_funcs: for identifier, py_func in py_funcs:
arg_str = inspect.formatargspec(*inspect.getargspec(py_func)) pyfunc2sphinx(" ", fw, identifier, py_func, is_class=True)
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(" .. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
if py_func.__doc__:
write_indented_lines(" ", fw, py_func.__doc__)
fw("\n")
del py_funcs, py_func del py_funcs, py_func
if struct.references: if struct.references: