1 # ***** BEGIN GPL LICENSE BLOCK *****
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # Contributor(s): Campbell Barton
19 # #**** END GPL LICENSE BLOCK #****
28 - Run this script from blenders root path once you have compiled blender
30 ./blender.bin -b -P doc/python_api/sphinx_doc_gen.py
32 This will generate python files in doc/python_api/sphinx-in/,
33 assuming that ./blender.bin is or links to the blender executable
35 - Generate html docs by running...
37 sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
39 assuming that you have sphinx 1.0.7 installed
43 - After you have built doc/python_api/sphinx-in (see above), run:
45 sphinx-build -b latex doc/python_api/sphinx-in doc/python_api/sphinx-out
46 cd doc/python_api/sphinx-out
50 # Switch for quick testing
54 FILTER_BPY_TYPES = None
58 # for testing so doc-builds dont take so long.
67 # "bpy.types", # supports filtering
68 "bpy.ops", # supports filtering
77 FILTER_BPY_TYPES = ("PropertyGroup", "Panel", "Menu", "Operator", "RenderEngine") # allow
78 FILTER_BPY_OPS = ("import.scene", ) # allow
82 rm -rf /b/doc/python_api/sphinx-* && \
83 ./blender.bin --background --factory-startup --python doc/python_api/sphinx_doc_gen.py && \
84 sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
89 # import rpdb2; rpdb2.start_embedded_debugger('test')
96 # lame, python wont give some access
97 ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])
98 MethodDescriptorType = type(dict.get)
99 GetSetDescriptorType = type(int.real)
102 EXAMPLE_SET_USED = set()
104 _BPY_STRUCT_FAKE = "bpy_struct"
105 _BPY_PROP_COLLECTION_FAKE = "bpy_prop_collection"
106 _BPY_FULL_REBUILD = False
108 if _BPY_PROP_COLLECTION_FAKE:
109 _BPY_PROP_COLLECTION_ID = ":class:`%s`" % _BPY_PROP_COLLECTION_FAKE
111 _BPY_PROP_COLLECTION_ID = "collection"
113 def undocumented_message(module_name, type_name, identifier):
114 if str(type_name).startswith('<module'):
115 preloadtitle = '%s.%s' % (module_name, identifier)
117 preloadtitle = '%s.%s.%s' % (module_name, type_name, identifier)
118 message = "Undocumented (`contribute "\
119 "<http://wiki.blender.org/index.php/Dev:2.5/Py/API/Documentation/Contribute"\
120 "?action=edit§ion=new&preload=Dev:2.5/Py/API/Documentation/Contribute/Howto-message"\
121 "&preloadtitle=%s>`_)\n\n" % preloadtitle
127 Converts values to strings for the range directive.
128 (unused function it seems)
134 elif type(val) == float:
140 def example_extract_docstring(filepath):
141 file = open(filepath, 'r')
142 line = file.readline()
145 if line.startswith('"""'): # assume nothing here
151 for line in file.readlines():
153 if line.startswith('"""'):
156 text.append(line.rstrip())
160 return "\n".join(text), line_no
163 def write_title(fw, text, heading_char):
164 fw("%s\n%s\n\n" % (text, len(text) * heading_char))
167 def write_example_ref(ident, fw, example_id, ext="py"):
168 if example_id in EXAMPLE_SET:
170 # extract the comment
171 filepath = "../examples/%s.%s" % (example_id, ext)
172 filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
174 text, line_no = example_extract_docstring(filepath_full)
176 for line in text.split("\n"):
177 fw("%s\n" % (ident + line).rstrip())
180 fw("%s.. literalinclude:: %s\n" % (ident, filepath))
182 fw("%s :lines: %d-\n" % (ident, line_no))
184 EXAMPLE_SET_USED.add(example_id)
187 print("\tskipping example:", example_id)
189 # Support for numbered files bpy.types.Operator -> bpy.types.Operator.1.py
192 example_id_num = "%s.%d" % (example_id, i)
193 if example_id_num in EXAMPLE_SET:
194 write_example_ref(ident, fw, example_id_num, ext)
200 def write_indented_lines(ident, fn, text, strip=True):
202 Apply same indentation to all lines in a multilines text.
206 for l in text.split("\n"):
208 fn(ident + l.strip() + "\n")
213 def pymethod2sphinx(ident, fw, identifier, py_func):
215 class method to sphinx
217 arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
218 if arg_str.startswith("(self, "):
219 arg_str = "(" + arg_str[7:]
221 elif arg_str.startswith("(cls, "):
222 arg_str = "(" + arg_str[6:]
223 func_type = "classmethod"
225 func_type = "staticmethod"
227 fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
229 write_indented_lines(ident + " ", fw, py_func.__doc__)
233 def pyfunc2sphinx(ident, fw, identifier, py_func, is_class=True):
235 function or class method to sphinx
237 arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
240 func_type = "function"
242 # ther rest are class methods
243 elif arg_str.startswith("(self, "):
244 arg_str = "(" + arg_str[7:]
246 elif arg_str.startswith("(cls, "):
247 arg_str = "(" + arg_str[6:]
248 func_type = "classmethod"
250 func_type = "staticmethod"
252 fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
254 write_indented_lines(ident + " ", fw, py_func.__doc__.strip())
258 def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
259 if identifier.startswith("_"):
264 doc = undocumented_message(module_name, type_name, identifier)
266 if type(descr) == GetSetDescriptorType:
267 fw(ident + ".. attribute:: %s\n\n" % identifier)
268 write_indented_lines(ident + " ", fw, doc, False)
269 elif type(descr) in (MethodDescriptorType, ClassMethodDescriptorType):
270 write_indented_lines(ident, fw, doc, False)
272 raise TypeError("type was not GetSetDescriptorType, MethodDescriptorType or ClassMethodDescriptorType")
274 write_example_ref(ident, fw, module_name + "." + type_name + "." + identifier)
278 def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True):
280 c defined function to sphinx.
283 # dump the docstring, assume its formatted correctly
285 write_indented_lines(ident, fw, py_func.__doc__, False)
288 fw(ident + ".. function:: %s()\n\n" % identifier)
289 fw(ident + " " + undocumented_message(module_name, type_name, identifier))
292 write_example_ref(ident + " ", fw, module_name + "." + type_name + "." + identifier)
294 write_example_ref(ident + " ", fw, module_name + "." + identifier)
299 def pyprop2sphinx(ident, fw, identifier, py_prop):
301 python property to sphinx
303 # readonly properties use "data" directive, variables use "attribute" directive
304 if py_prop.fset is None:
305 fw(ident + ".. data:: %s\n\n" % identifier)
307 fw(ident + ".. attribute:: %s\n\n" % identifier)
308 write_indented_lines(ident + " ", fw, py_prop.__doc__)
309 if py_prop.fset is None:
310 fw(ident + " (readonly)\n\n")
313 def pymodule2sphinx(BASEPATH, module_name, module, title):
315 attribute_set = set()
316 filepath = os.path.join(BASEPATH, module_name + ".rst")
318 file = open(filepath, "w")
322 write_title(fw, title, "=")
324 fw(".. module:: %s\n\n" % module_name)
327 # Note, may contain sphinx syntax, dont mangle!
328 fw(module.__doc__.strip())
331 write_example_ref("", fw, module_name)
333 # write members of the module
334 # only tested with PyStructs which are not exactly modules
335 for key, descr in sorted(type(module).__dict__.items()):
336 if key.startswith("__"):
338 # naughty, we also add getset's into PyStructs, this is not typical py but also not incorrect.
339 if type(descr) == types.GetSetDescriptorType: # 'bpy_app_type' name is only used for examples and messages
340 py_descr2sphinx("", fw, descr, module_name, "bpy_app_type", key)
341 attribute_set.add(key)
342 for key, descr in sorted(type(module).__dict__.items()):
343 if key.startswith("__"):
346 if type(descr) == types.MemberDescriptorType:
348 fw(".. data:: %s\n\n" % key)
349 write_indented_lines(" ", fw, descr.__doc__, False)
350 attribute_set.add(key)
356 for attribute in sorted(dir(module)):
357 if not attribute.startswith("_"):
359 if attribute in attribute_set:
362 if attribute.startswith("n_"): # annoying exception, needed for bpy.app
365 value = getattr(module, attribute)
367 value_type = type(value)
369 if value_type == types.FunctionType:
370 pyfunc2sphinx("", fw, attribute, value, is_class=False)
371 elif value_type in (types.BuiltinMethodType, types.BuiltinFunctionType): # both the same at the moment but to be future proof
372 # note: can't get args from these, so dump the string as is
373 # this means any module used like this must have fully formatted docstrings.
374 py_c_func2sphinx("", fw, module_name, None, attribute, value, is_class=False)
375 elif value_type == type:
376 classes.append((attribute, value))
377 elif value_type in (bool, int, float, str, tuple):
378 # constant, not much fun we can do here except to list it.
379 # TODO, figure out some way to document these!
380 fw(".. data:: %s\n\n" % attribute)
381 write_indented_lines(" ", fw, "constant value %s" % repr(value), False)
384 print("\tnot documenting %s.%s" % (module_name, attribute))
387 attribute_set.add(attribute)
388 # TODO, more types...
390 # write collected classes now
391 for (type_name, value) in classes:
392 # May need to be its own function
393 fw(".. class:: %s\n\n" % type_name)
395 write_indented_lines(" ", fw, value.__doc__, False)
397 write_example_ref(" ", fw, module_name + "." + type_name)
399 descr_items = [(key, descr) for key, descr in sorted(value.__dict__.items()) if not key.startswith("__")]
401 for key, descr in descr_items:
402 if type(descr) == ClassMethodDescriptorType:
403 py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
405 for key, descr in descr_items:
406 if type(descr) == MethodDescriptorType:
407 py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
409 for key, descr in descr_items:
410 if type(descr) == GetSetDescriptorType:
411 py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
418 def pycontext2sphinx(BASEPATH):
419 # Only use once. very irregular
421 filepath = os.path.join(BASEPATH, "bpy.context.rst")
422 file = open(filepath, "w")
424 fw("Context Access (bpy.context)\n")
425 fw("============================\n\n")
426 fw(".. module:: bpy.context\n")
428 fw("The context members available depend on the area of blender which is currently being accessed.\n")
430 fw("Note that all context values are readonly, but may be modified through the data api or by running operators\n\n")
432 # nasty, get strings directly from blender because there is no other way to get it
436 "screen_context_dir",
437 "view3d_context_dir",
438 "buttons_context_dir",
444 # Changes in blender will force errors here
446 "active_base": ("ObjectBase", False),
447 "active_bone": ("Bone", False),
448 "active_object": ("Object", False),
449 "active_pose_bone": ("PoseBone", False),
450 "armature": ("Armature", False),
451 "bone": ("Bone", False),
452 "brush": ("Brush", False),
453 "camera": ("Camera", False),
454 "cloth": ("ClothModifier", False),
455 "collision": ("CollisionModifier", False),
456 "curve": ("Curve", False),
457 "edit_bone": ("EditBone", False),
458 "edit_image": ("Image", False),
459 "edit_object": ("Object", False),
460 "edit_text": ("Text", False),
461 "editable_bones": ("EditBone", True),
462 "fluid": ("FluidSimulationModifier", False),
463 "lamp": ("Lamp", False),
464 "lattice": ("Lattice", False),
465 "material": ("Material", False),
466 "material_slot": ("MaterialSlot", False),
467 "mesh": ("Mesh", False),
468 "meta_ball": ("MetaBall", False),
469 "object": ("Object", False),
470 "particle_edit_object": ("Object", False),
471 "particle_system": ("ParticleSystem", False),
472 "particle_system_editable": ("ParticleSystem", False),
473 "pose_bone": ("PoseBone", False),
474 "scene": ("Scene", False),
475 "sculpt_object": ("Object", False),
476 "selectable_bases": ("ObjectBase", True),
477 "selectable_objects": ("Object", True),
478 "selected_bases": ("ObjectBase", True),
479 "selected_bones": ("Bone", True),
480 "selected_editable_bases": ("ObjectBase", True),
481 "selected_editable_bones": ("Bone", True),
482 "selected_editable_objects": ("Object", True),
483 "selected_editable_sequences": ("Sequence", True),
484 "selected_nodes": ("Node", True),
485 "selected_objects": ("Object", True),
486 "selected_pose_bones": ("PoseBone", True),
487 "selected_sequences": ("Sequence", True),
488 "sequences": ("Sequence", True),
489 "smoke": ("SmokeModifier", False),
490 "soft_body": ("SoftBodyModifier", False),
491 "texture": ("Texture", False),
492 "texture_paint_object": ("Object", False),
493 "texture_slot": ("MaterialTextureSlot", False),
494 "vertex_paint_object": ("Object", False),
495 "visible_bases": ("ObjectBase", True),
496 "visible_bones": ("Object", True),
497 "visible_objects": ("Object", True),
498 "visible_pose_bones": ("PoseBone", True),
499 "weight_paint_object": ("Object", False),
500 "world": ("World", False),
504 blend_cdll = ctypes.CDLL("")
505 for ctx_str in context_strings:
506 subsection = "%s Context" % ctx_str.split("_")[0].title()
507 fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * '-')))
509 attr = ctypes.addressof(getattr(blend_cdll, ctx_str))
510 c_char_p_p = ctypes.POINTER(ctypes.c_char_p)
511 char_array = c_char_p_p.from_address(attr)
513 while char_array[i] is not None:
514 member = ctypes.string_at(char_array[i]).decode()
515 fw(".. data:: %s\n\n" % member)
516 member_type, is_seq = type_map[member]
517 fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type))
521 # generate typemap...
522 # for member in sorted(unique):
523 # print(' "%s": ("", False),' % member)
524 if len(type_map) > len(unique):
525 raise Exception("Some types are not used: %s" % str([member for member in type_map if member not in unique]))
527 pass # will have raised an error above
532 def pyrna2sphinx(BASEPATH):
533 """ bpy.types and bpy.ops
535 structs, funcs, ops, props = rna_info.BuildRNAInfo()
536 if FILTER_BPY_TYPES is not None:
537 structs = {k: v for k, v in structs.items() if k[1] in FILTER_BPY_TYPES}
539 if FILTER_BPY_OPS is not None:
540 ops = {k: v for k, v in ops.items() if v.module_name in FILTER_BPY_OPS}
542 def write_param(ident, fw, prop, is_return=False):
546 kwargs = {"as_ret": True}
551 kwargs = {"as_arg": True}
552 identifier = " %s" % prop.identifier
554 kwargs["class_fmt"] = ":class:`%s`"
556 kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
558 type_descr = prop.get_type_description(**kwargs)
559 if prop.name or prop.description:
560 fw(ident + ":%s%s: %s\n" % (id_name, identifier, ", ".join(val for val in (prop.name, prop.description) if val)))
561 fw(ident + ":%s%s: %s\n" % (id_type, identifier, type_descr))
563 def write_struct(struct):
564 #if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
567 #if not struct.identifier == "Object":
570 filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % struct.identifier)
571 file = open(filepath, "w")
574 base_id = getattr(struct.base, "identifier", "")
578 base_id = _BPY_STRUCT_FAKE
581 title = "%s(%s)" % (struct.identifier, base_id)
583 title = struct.identifier
585 write_title(fw, title, "=")
587 fw(".. module:: bpy.types\n\n")
590 write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
592 base_ids = [base.identifier for base in struct.get_bases()]
595 base_ids.append(_BPY_STRUCT_FAKE)
600 if len(base_ids) > 1:
601 fw("base classes --- ")
603 fw("base class --- ")
605 fw(", ".join((":class:`%s`" % base_id) for base_id in base_ids))
608 subclass_ids = [s.identifier for s in structs.values() if s.base is struct if not rna_info.rna_id_ignore(s.identifier)]
610 fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in subclass_ids) + "\n\n")
612 base_id = getattr(struct.base, "identifier", "")
616 base_id = _BPY_STRUCT_FAKE
619 fw(".. class:: %s(%s)\n\n" % (struct.identifier, base_id))
621 fw(".. class:: %s\n\n" % struct.identifier)
623 fw(" %s\n\n" % struct.description)
625 # properties sorted in alphabetical order
626 sorted_struct_properties = struct.properties[:]
627 sorted_struct_properties.sort(key=lambda prop: prop.identifier)
629 for prop in sorted_struct_properties:
630 type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
631 # readonly properties use "data" directive, variables properties use "attribute" directive
632 if 'readonly' in type_descr:
633 fw(" .. data:: %s\n\n" % prop.identifier)
635 fw(" .. attribute:: %s\n\n" % prop.identifier)
637 fw(" %s\n\n" % prop.description)
638 fw(" :type: %s\n\n" % type_descr)
641 py_properties = struct.get_py_properties()
643 for identifier, py_prop in py_properties:
644 pyprop2sphinx(" ", fw, identifier, py_prop)
645 del py_properties, py_prop
647 for func in struct.functions:
648 args_str = ", ".join(prop.get_arg_default(force=False) for prop in func.args)
650 fw(" .. %s:: %s(%s)\n\n" % ("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
651 fw(" %s\n\n" % func.description)
653 for prop in func.args:
654 write_param(" ", fw, prop)
656 if len(func.return_values) == 1:
657 write_param(" ", fw, func.return_values[0], is_return=True)
658 elif func.return_values: # multiple return values
659 fw(" :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
660 for prop in func.return_values:
661 type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
662 descr = prop.description
665 fw(" `%s`, %s, %s\n\n" % (prop.identifier, descr, type_descr))
670 py_funcs = struct.get_py_functions()
673 for identifier, py_func in py_funcs:
674 pyfunc2sphinx(" ", fw, identifier, py_func, is_class=True)
675 del py_funcs, py_func
677 py_funcs = struct.get_py_c_functions()
680 for identifier, py_func in py_funcs:
681 py_c_func2sphinx(" ", fw, "bpy.types", struct.identifier, identifier, py_func, is_class=True)
685 if struct.base or _BPY_STRUCT_FAKE:
686 bases = list(reversed(struct.get_bases()))
692 descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
695 for key, descr in descr_items:
696 if type(descr) == GetSetDescriptorType:
697 lines.append(" * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
700 for prop in base.properties:
701 lines.append(" * :class:`%s.%s`\n" % (base.identifier, prop.identifier))
703 for identifier, py_prop in base.get_py_properties():
704 lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
706 for identifier, py_prop in base.get_py_properties():
707 lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
710 fw(".. rubric:: Inherited Properties\n\n")
713 fw(" :columns: 2\n\n")
723 for key, descr in descr_items:
724 if type(descr) == MethodDescriptorType:
725 lines.append(" * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
728 for func in base.functions:
729 lines.append(" * :class:`%s.%s`\n" % (base.identifier, func.identifier))
730 for identifier, py_func in base.get_py_functions():
731 lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
734 fw(".. rubric:: Inherited Functions\n\n")
737 fw(" :columns: 2\n\n")
745 if struct.references:
746 # use this otherwise it gets in the index for a normal heading.
747 fw(".. rubric:: References\n\n")
750 fw(" :columns: 2\n\n")
752 for ref in struct.references:
753 ref_split = ref.split(".")
754 if len(ref_split) > 2:
755 ref = ref_split[-2] + "." + ref_split[-1]
756 fw(" * :class:`%s`\n" % ref)
759 # docs last?, disable for now
760 # write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
763 if "bpy.types" not in EXCLUDE_MODULES:
764 for struct in structs.values():
765 # TODO, rna_info should filter these out!
766 if "_OT_" in struct.identifier:
770 def fake_bpy_type(class_value, class_name, descr_str, use_subclasses=True):
771 filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % class_name)
772 file = open(filepath, "w")
775 write_title(fw, class_name, "=")
777 fw(".. module:: bpy.types\n")
781 subclass_ids = [s.identifier for s in structs.values() if s.base is None if not rna_info.rna_id_ignore(s.identifier)]
783 fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
785 fw(".. class:: %s\n\n" % class_name)
786 fw(" %s\n\n" % descr_str)
788 fw(" Note that bpy.types.%s is not actually available from within blender, it only exists for the purpose of documentation.\n\n" % class_name)
790 descr_items = [(key, descr) for key, descr in sorted(class_value.__dict__.items()) if not key.startswith("__")]
792 for key, descr in descr_items:
793 if type(descr) == MethodDescriptorType: # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
794 py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
796 for key, descr in descr_items:
797 if type(descr) == GetSetDescriptorType:
798 py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
803 class_value = bpy.types.Struct.__bases__[0]
804 fake_bpy_type(class_value, _BPY_STRUCT_FAKE, "built-in base class for all classes in bpy.types.", use_subclasses=True)
806 if _BPY_PROP_COLLECTION_FAKE:
807 class_value = bpy.data.objects.__class__
808 fake_bpy_type(class_value, _BPY_PROP_COLLECTION_FAKE, "built-in class used for all collections.", use_subclasses=False)
812 API_BASEURL = "https://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts"
815 for op in ops.values():
816 op_modules.setdefault(op.module_name, []).append(op)
819 for op_module_name, ops_mod in op_modules.items():
820 filepath = os.path.join(BASEPATH, "bpy.ops.%s.rst" % op_module_name)
821 file = open(filepath, "w")
824 title = "%s Operators" % op_module_name.replace("_", " ").title()
826 write_title(fw, title, "=")
828 fw(".. module:: bpy.ops.%s\n\n" % op_module_name)
830 ops_mod.sort(key=lambda op: op.func_name)
833 args_str = ", ".join(prop.get_arg_default(force=True) for prop in op.args)
834 fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str))
836 # if the description isn't valid, we output the standard warning
837 # with a link to the wiki so that people can help
838 if not op.description or op.description == "(undocumented operator)":
839 operator_description = undocumented_message('bpy.ops', op.module_name, op.func_name)
841 operator_description = op.description
843 fw(" %s\n\n" % operator_description)
845 write_param(" ", fw, prop)
849 location = op.get_location()
850 if location != (None, None):
851 fw(" :file: `%s <%s/%s>`_:%d\n\n" % (location[0], API_BASEURL, location[0], location[1]))
855 if "bpy.ops" not in EXCLUDE_MODULES:
859 def rna2sphinx(BASEPATH):
866 # conf.py - empty for now
867 filepath = os.path.join(BASEPATH, "conf.py")
868 file = open(filepath, "w")
871 version_string = ".".join(str(v) for v in bpy.app.version)
872 if bpy.app.build_revision != "Unknown":
873 version_string = version_string + " r" + bpy.app.build_revision
876 version_string_fp = "_".join(str(v) for v in bpy.app.version)
878 fw("project = 'Blender'\n")
879 # fw("master_doc = 'index'\n")
880 fw("copyright = u'Blender Foundation'\n")
881 fw("version = '%s - UNSTABLE API'\n" % version_string)
882 fw("release = '%s - UNSTABLE API'\n" % version_string)
883 fw("html_theme = 'blender-org'\n")
884 fw("html_theme_path = ['../']\n")
885 fw("html_favicon = 'favicon.ico'\n")
886 # not helpful since the source us generated, adds to upload size.
887 fw("html_copy_source = False\n")
889 # needed for latex, pdf gen
890 fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n")
891 fw("latex_paper_size = 'a4paper'\n")
894 # main page needed for sphinx (index.html)
895 filepath = os.path.join(BASEPATH, "contents.rst")
896 file = open(filepath, "w")
899 fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
900 fw(" Blender Documentation contents\n")
901 fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
903 fw("This document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date))
905 fw("| An introduction to Blender and Python can be found at `Quickstart Intro <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro>`_,\n")
906 fw("| For a more general explanation of blender/python see the `API Overview <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Overview>`_\n")
908 fw("`A PDF version of this document is also available <blender_python_reference_%s.pdf>`_\n" % version_string_fp)
910 fw(".. warning:: The Python API in Blender is **UNSTABLE**, It should only be used for testing, any script written now may break in future releases.\n")
912 fw(" The following areas are subject to change.\n")
913 fw(" * operator names and arguments\n")
914 fw(" * render api\n")
915 fw(" * function calls with the data api (any function calls with values accessed from bpy.data), including functions for importing and exporting meshes\n")
916 fw(" * class registration (Operator, Panels, Menus, Headers)\n")
917 fw(" * modules: bpy.props, blf)\n")
918 fw(" * members in the bpy.context have to be reviewed\n")
919 fw(" * python defined modal operators, especially drawing callbacks are highly experemental\n")
921 fw(" These parts of the API are relatively stable and are unlikely to change significantly\n")
922 fw(" * data API, access to attributes of blender data such as mesh verts, material color, timeline frames and scene objects\n")
923 fw(" * user interface functions for defining buttons, creation of menus, headers, panels\n")
924 fw(" * modules: bgl and mathutils\n")
925 fw(" * game engine modules\n")
928 fw("===================\n")
929 fw("Application Modules\n")
930 fw("===================\n")
933 fw(" :maxdepth: 1\n\n")
934 if "bpy.context" not in EXCLUDE_MODULES:
935 fw(" bpy.context.rst\n\n") # note: not actually a module
936 if "bpy.data" not in EXCLUDE_MODULES:
937 fw(" bpy.data.rst\n\n") # note: not actually a module
938 if "bpy.ops" not in EXCLUDE_MODULES:
939 fw(" bpy.ops.rst\n\n")
940 if "bpy.types" not in EXCLUDE_MODULES:
941 fw(" bpy.types.rst\n\n")
944 if "bpy.utils" not in EXCLUDE_MODULES:
945 fw(" bpy.utils.rst\n\n")
946 if "bpy.path" not in EXCLUDE_MODULES:
947 fw(" bpy.path.rst\n\n")
948 if "bpy.app" not in EXCLUDE_MODULES:
949 fw(" bpy.app.rst\n\n")
952 if "bpy.props" not in EXCLUDE_MODULES:
953 fw(" bpy.props.rst\n\n")
955 fw("==================\n")
956 fw("Standalone Modules\n")
957 fw("==================\n")
960 fw(" :maxdepth: 1\n\n")
962 if "mathutils" not in EXCLUDE_MODULES:
963 fw(" mathutils.rst\n\n")
964 if "mathutils.geometry" not in EXCLUDE_MODULES:
965 fw(" mathutils.geometry.rst\n\n")
968 if "blf" not in EXCLUDE_MODULES:
970 if "aud" not in EXCLUDE_MODULES:
974 if "bge" not in EXCLUDE_MODULES:
975 fw("===================\n")
976 fw("Game Engine Modules\n")
977 fw("===================\n")
980 fw(" :maxdepth: 1\n\n")
981 fw(" bge.types.rst\n\n")
982 fw(" bge.logic.rst\n\n")
983 fw(" bge.render.rst\n\n")
984 fw(" bge.events.rst\n\n")
989 if "bpy.ops" not in EXCLUDE_MODULES:
990 filepath = os.path.join(BASEPATH, "bpy.ops.rst")
991 file = open(filepath, "w")
993 fw("Operators (bpy.ops)\n")
994 fw("===================\n\n")
995 write_example_ref("", fw, "bpy.ops")
1001 if "bpy.types" not in EXCLUDE_MODULES:
1002 filepath = os.path.join(BASEPATH, "bpy.types.rst")
1003 file = open(filepath, "w")
1005 fw("Types (bpy.types)\n")
1006 fw("=================\n\n")
1007 fw(".. toctree::\n")
1009 fw(" bpy.types.*\n\n")
1012 if "bpy.data" not in EXCLUDE_MODULES:
1013 # not actually a module, only write this file so we
1014 # can reference in the TOC
1015 filepath = os.path.join(BASEPATH, "bpy.data.rst")
1016 file = open(filepath, "w")
1018 fw("Data Access (bpy.data)\n")
1019 fw("======================\n\n")
1020 fw(".. module:: bpy\n")
1022 fw("This module is used for all blender/python access.\n")
1024 fw(".. data:: data\n")
1026 fw(" Access to blenders internal data\n")
1028 fw(" :type: :class:`bpy.types.BlendData`\n")
1030 fw(".. literalinclude:: ../examples/bpy.data.py\n")
1033 EXAMPLE_SET_USED.add("bpy.data")
1037 if "bpy.context" not in EXCLUDE_MODULES:
1038 # one of a kind, context doc (uses ctypes to extract info!)
1039 pycontext2sphinx(BASEPATH)
1042 if "bpy.utils" not in EXCLUDE_MODULES:
1043 from bpy import utils as module
1044 pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities (bpy.utils)")
1046 if "bpy.path" not in EXCLUDE_MODULES:
1047 from bpy import path as module
1048 pymodule2sphinx(BASEPATH, "bpy.path", module, "Path Utilities (bpy.path)")
1051 if "bpy.app" not in EXCLUDE_MODULES:
1052 from bpy import app as module
1053 pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data (bpy.app)")
1055 if "bpy.props" not in EXCLUDE_MODULES:
1056 from bpy import props as module
1057 pymodule2sphinx(BASEPATH, "bpy.props", module, "Property Definitions (bpy.props)")
1059 if "mathutils" not in EXCLUDE_MODULES:
1060 import mathutils as module
1061 pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities (mathutils)")
1063 if "mathutils.geometry" not in EXCLUDE_MODULES:
1064 import mathutils.geometry as module
1065 pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities (mathutils.geometry)")
1067 if "mathutils.geometry" not in EXCLUDE_MODULES:
1068 import blf as module
1069 pymodule2sphinx(BASEPATH, "blf", module, "Font Drawing (blf)")
1072 #import bgl as module
1073 #pymodule2sphinx(BASEPATH, "bgl", module, "Blender OpenGl wrapper (bgl)")
1076 if "aud" not in EXCLUDE_MODULES:
1077 import aud as module
1078 pymodule2sphinx(BASEPATH, "aud", module, "Audio System (aud)")
1083 # copy2 keeps time/date stamps
1084 if "bge" not in EXCLUDE_MODULES:
1085 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.types.rst"), BASEPATH)
1086 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.logic.rst"), BASEPATH)
1087 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.render.rst"), BASEPATH)
1088 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.events.rst"), BASEPATH)
1091 filepath = os.path.join(BASEPATH, "bpy.rst")
1092 file = open(filepath, "w")
1097 title = ":mod:`bpy` --- Blender Python Module"
1099 write_title(fw, title, "=")
1101 fw(".. module:: bpy.types\n\n")
1104 # bpy.types and bpy.ops
1105 pyrna2sphinx(BASEPATH)
1112 if 'bpy' not in dir():
1113 print("\nError, this script must run from inside blender2.5")
1114 print(script_help_msg)
1118 script_dir = os.path.dirname(__file__)
1119 path_in = os.path.join(script_dir, "sphinx-in")
1120 path_out = os.path.join(script_dir, "sphinx-out")
1121 path_examples = os.path.join(script_dir, "examples")
1122 # only for partial updates
1123 path_in_tmp = path_in + "-tmp"
1125 if not os.path.exists(path_in):
1128 for f in os.listdir(path_examples):
1129 if f.endswith(".py"):
1130 EXAMPLE_SET.add(os.path.splitext(f)[0])
1132 # only for full updates
1133 if _BPY_FULL_REBUILD:
1134 shutil.rmtree(path_in, True)
1135 shutil.rmtree(path_out, True)
1137 # write here, then move
1138 shutil.rmtree(path_in_tmp, True)
1140 rna2sphinx(path_in_tmp)
1142 if not _BPY_FULL_REBUILD:
1145 # now move changed files from 'path_in_tmp' --> 'path_in'
1146 file_list_path_in = set(os.listdir(path_in))
1147 file_list_path_in_tmp = set(os.listdir(path_in_tmp))
1149 # remove deprecated files that have been removed.
1150 for f in sorted(file_list_path_in):
1151 if f not in file_list_path_in_tmp:
1152 print("\tdeprecated: %s" % f)
1153 os.remove(os.path.join(path_in, f))
1155 # freshen with new files.
1156 for f in sorted(file_list_path_in_tmp):
1157 f_from = os.path.join(path_in_tmp, f)
1158 f_to = os.path.join(path_in, f)
1161 if f in file_list_path_in:
1162 if filecmp.cmp(f_from, f_to):
1166 print("\tupdating: %s" % f)
1167 shutil.copy(f_from, f_to)
1169 print("\tkeeping: %s" % f) # eh, not that useful'''
1171 EXAMPLE_SET_UNUSED = EXAMPLE_SET - EXAMPLE_SET_USED
1172 if EXAMPLE_SET_UNUSED:
1173 print("\nUnused examples found in '%s'..." % path_examples)
1174 for f in EXAMPLE_SET_UNUSED:
1176 print(" %d total\n" % len(EXAMPLE_SET_UNUSED))
1181 if __name__ == '__main__':