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 --background --python doc/python_api/sphinx_doc_gen.py
32 This will generate python files in doc/python_api/sphinx-in/
33 providing ./blender.bin is or links to the blender executable
35 - Generate html docs by running...
38 sphinx-build sphinx-in sphinx-out
40 This requires sphinx 1.0.7 to be installed.
44 - After you have built doc/python_api/sphinx-in (see above), run:
46 sphinx-build -b latex doc/python_api/sphinx-in doc/python_api/sphinx-out
47 cd doc/python_api/sphinx-out
51 # Check we're running in blender
52 if __import__("sys").modules.get("bpy") is None:
53 print("\nError, this script must run from inside blender2.5")
54 print(script_help_msg)
60 # Switch for quick testing
64 FILTER_BPY_TYPES = None
68 # for testing so doc-builds dont take so long.
77 "bpy.types", # supports filtering
78 "bpy.ops", # supports filtering
88 FILTER_BPY_TYPES = ("bpy_struct", "Panel", "Menu", "Operator", "RenderEngine") # allow
89 FILTER_BPY_OPS = ("import.scene", ) # allow
93 rm -rf /b/doc/python_api/sphinx-* && \
94 ./blender.bin --background --factory-startup --python doc/python_api/sphinx_doc_gen.py && \
95 sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
100 # import rpdb2; rpdb2.start_embedded_debugger('test')
107 # lame, python wont give some access
108 ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])
109 MethodDescriptorType = type(dict.get)
110 GetSetDescriptorType = type(int.real)
113 EXAMPLE_SET_USED = set()
115 _BPY_STRUCT_FAKE = "bpy_struct"
116 _BPY_PROP_COLLECTION_FAKE = "bpy_prop_collection"
117 _BPY_FULL_REBUILD = False
119 if _BPY_PROP_COLLECTION_FAKE:
120 _BPY_PROP_COLLECTION_ID = ":class:`%s`" % _BPY_PROP_COLLECTION_FAKE
122 _BPY_PROP_COLLECTION_ID = "collection"
125 def undocumented_message(module_name, type_name, identifier):
126 if str(type_name).startswith('<module'):
127 preloadtitle = '%s.%s' % (module_name, identifier)
129 preloadtitle = '%s.%s.%s' % (module_name, type_name, identifier)
130 message = "Undocumented (`contribute "\
131 "<http://wiki.blender.org/index.php/Dev:2.5/Py/API/Documentation/Contribute"\
132 "?action=edit§ion=new&preload=Dev:2.5/Py/API/Documentation/Contribute/Howto-message"\
133 "&preloadtitle=%s>`_)\n\n" % preloadtitle
139 Converts values to strings for the range directive.
140 (unused function it seems)
146 elif type(val) == float:
152 def example_extract_docstring(filepath):
153 file = open(filepath, 'r')
154 line = file.readline()
157 if line.startswith('"""'): # assume nothing here
163 for line in file.readlines():
165 if line.startswith('"""'):
168 text.append(line.rstrip())
172 return "\n".join(text), line_no
175 def write_title(fw, text, heading_char):
176 fw("%s\n%s\n\n" % (text, len(text) * heading_char))
179 def write_example_ref(ident, fw, example_id, ext="py"):
180 if example_id in EXAMPLE_SET:
182 # extract the comment
183 filepath = "../examples/%s.%s" % (example_id, ext)
184 filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
186 text, line_no = example_extract_docstring(filepath_full)
188 for line in text.split("\n"):
189 fw("%s\n" % (ident + line).rstrip())
192 fw("%s.. literalinclude:: %s\n" % (ident, filepath))
194 fw("%s :lines: %d-\n" % (ident, line_no))
196 EXAMPLE_SET_USED.add(example_id)
199 print("\tskipping example:", example_id)
201 # Support for numbered files bpy.types.Operator -> bpy.types.Operator.1.py
204 example_id_num = "%s.%d" % (example_id, i)
205 if example_id_num in EXAMPLE_SET:
206 write_example_ref(ident, fw, example_id_num, ext)
212 def write_indented_lines(ident, fn, text, strip=True):
214 Apply same indentation to all lines in a multilines text.
219 lines = text.split("\n")
221 # strip empty lines from the start/end
222 while lines and not lines[0].strip():
224 while lines and not lines[-1].strip():
231 ident_strip = min(ident_strip, len(l) - len(l.lstrip()))
233 fn(ident + l[ident_strip:] + "\n")
239 def pymethod2sphinx(ident, fw, identifier, py_func):
241 class method to sphinx
243 arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
244 if arg_str.startswith("(self, "):
245 arg_str = "(" + arg_str[7:]
247 elif arg_str.startswith("(cls, "):
248 arg_str = "(" + arg_str[6:]
249 func_type = "classmethod"
251 func_type = "staticmethod"
253 fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
255 write_indented_lines(ident + " ", fw, py_func.__doc__)
259 def pyfunc2sphinx(ident, fw, identifier, py_func, is_class=True):
261 function or class method to sphinx
263 arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
266 func_type = "function"
268 # ther rest are class methods
269 elif arg_str.startswith("(self, "):
270 arg_str = "(" + arg_str[7:]
272 elif arg_str.startswith("(cls, "):
273 arg_str = "(" + arg_str[6:]
274 func_type = "classmethod"
276 func_type = "staticmethod"
278 fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
280 write_indented_lines(ident + " ", fw, py_func.__doc__)
284 def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
285 if identifier.startswith("_"):
290 doc = undocumented_message(module_name, type_name, identifier)
292 if type(descr) == GetSetDescriptorType:
293 fw(ident + ".. attribute:: %s\n\n" % identifier)
294 write_indented_lines(ident + " ", fw, doc, False)
296 elif type(descr) in (MethodDescriptorType, ClassMethodDescriptorType):
297 write_indented_lines(ident, fw, doc, False)
300 raise TypeError("type was not GetSetDescriptorType, MethodDescriptorType or ClassMethodDescriptorType")
302 write_example_ref(ident + " ", fw, module_name + "." + type_name + "." + identifier)
306 def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True):
308 c defined function to sphinx.
311 # dump the docstring, assume its formatted correctly
313 write_indented_lines(ident, fw, py_func.__doc__, False)
316 fw(ident + ".. function:: %s()\n\n" % identifier)
317 fw(ident + " " + undocumented_message(module_name, type_name, identifier))
320 write_example_ref(ident + " ", fw, module_name + "." + type_name + "." + identifier)
322 write_example_ref(ident + " ", fw, module_name + "." + identifier)
327 def pyprop2sphinx(ident, fw, identifier, py_prop):
329 python property to sphinx
331 # readonly properties use "data" directive, variables use "attribute" directive
332 if py_prop.fset is None:
333 fw(ident + ".. data:: %s\n\n" % identifier)
335 fw(ident + ".. attribute:: %s\n\n" % identifier)
336 write_indented_lines(ident + " ", fw, py_prop.__doc__)
337 if py_prop.fset is None:
338 fw(ident + " (readonly)\n\n")
341 def pymodule2sphinx(BASEPATH, module_name, module, title):
343 attribute_set = set()
344 filepath = os.path.join(BASEPATH, module_name + ".rst")
346 module_all = getattr(module, "__all__", None)
347 module_dir = sorted(dir(module))
350 module_dir = module_all
352 file = open(filepath, "w")
356 write_title(fw, "%s (%s)" % (title, module_name), "=")
358 fw(".. module:: %s\n\n" % module_name)
361 # Note, may contain sphinx syntax, dont mangle!
362 fw(module.__doc__.strip())
365 write_example_ref("", fw, module_name)
368 # we could also scan files but this ensures __all__ is used correctly
369 if module_all is not None:
373 for submod_name in module_all:
375 exec_str = "from %s import %s as submod" % (module.__name__, submod_name)
376 exec(exec_str, ns, ns)
377 submod = ns["submod"]
378 if type(submod) == types.ModuleType:
379 submod_ls.append((submod_name, submod))
386 fw(" :maxdepth: 1\n\n")
388 for submod_name, submod in submod_ls:
389 submod_name_full = "%s.%s" % (module_name, submod_name)
390 fw(" %s.rst\n\n" % submod_name_full)
392 pymodule2sphinx(BASEPATH, submod_name_full, submod, "%s submodule" % module_name)
394 # done writing submodules!
396 # write members of the module
397 # only tested with PyStructs which are not exactly modules
398 for key, descr in sorted(type(module).__dict__.items()):
399 if key.startswith("__"):
401 # naughty, we also add getset's into PyStructs, this is not typical py but also not incorrect.
402 if type(descr) == types.GetSetDescriptorType: # 'bpy_app_type' name is only used for examples and messages
403 py_descr2sphinx("", fw, descr, module_name, "bpy_app_type", key)
404 attribute_set.add(key)
405 for key, descr in sorted(type(module).__dict__.items()):
406 if key.startswith("__"):
409 if type(descr) == types.MemberDescriptorType:
411 fw(".. data:: %s\n\n" % key)
412 write_indented_lines(" ", fw, descr.__doc__, False)
414 attribute_set.add(key)
420 for attribute in module_dir:
421 if not attribute.startswith("_"):
422 if attribute in attribute_set:
425 if attribute.startswith("n_"): # annoying exception, needed for bpy.app
428 value = getattr(module, attribute)
430 value_type = type(value)
432 if value_type == types.FunctionType:
433 pyfunc2sphinx("", fw, attribute, value, is_class=False)
434 elif value_type in (types.BuiltinMethodType, types.BuiltinFunctionType): # both the same at the moment but to be future proof
435 # note: can't get args from these, so dump the string as is
436 # this means any module used like this must have fully formatted docstrings.
437 py_c_func2sphinx("", fw, module_name, None, attribute, value, is_class=False)
438 elif value_type == type:
439 classes.append((attribute, value))
440 elif value_type in (bool, int, float, str, tuple):
441 # constant, not much fun we can do here except to list it.
442 # TODO, figure out some way to document these!
443 fw(".. data:: %s\n\n" % attribute)
444 write_indented_lines(" ", fw, "constant value %s" % repr(value), False)
447 print("\tnot documenting %s.%s" % (module_name, attribute))
450 attribute_set.add(attribute)
451 # TODO, more types...
453 # write collected classes now
454 for (type_name, value) in classes:
455 # May need to be its own function
456 fw(".. class:: %s\n\n" % type_name)
458 write_indented_lines(" ", fw, value.__doc__, False)
460 write_example_ref(" ", fw, module_name + "." + type_name)
462 descr_items = [(key, descr) for key, descr in sorted(value.__dict__.items()) if not key.startswith("__")]
464 for key, descr in descr_items:
465 if type(descr) == ClassMethodDescriptorType:
466 py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
468 for key, descr in descr_items:
469 if type(descr) == MethodDescriptorType:
470 py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
472 for key, descr in descr_items:
473 if type(descr) == GetSetDescriptorType:
474 py_descr2sphinx(" ", fw, descr, module_name, type_name, key)
481 def pycontext2sphinx(BASEPATH):
482 # Only use once. very irregular
484 filepath = os.path.join(BASEPATH, "bpy.context.rst")
485 file = open(filepath, "w")
487 fw("Context Access (bpy.context)\n")
488 fw("============================\n\n")
489 fw(".. module:: bpy.context\n")
491 fw("The context members available depend on the area of blender which is currently being accessed.\n")
493 fw("Note that all context values are readonly, but may be modified through the data api or by running operators\n\n")
495 # nasty, get strings directly from blender because there is no other way to get it
499 "screen_context_dir",
500 "view3d_context_dir",
501 "buttons_context_dir",
507 # Changes in blender will force errors here
509 "active_base": ("ObjectBase", False),
510 "active_bone": ("Bone", False),
511 "active_object": ("Object", False),
512 "active_pose_bone": ("PoseBone", False),
513 "armature": ("Armature", False),
514 "bone": ("Bone", False),
515 "brush": ("Brush", False),
516 "camera": ("Camera", False),
517 "cloth": ("ClothModifier", False),
518 "collision": ("CollisionModifier", False),
519 "curve": ("Curve", False),
520 "edit_bone": ("EditBone", False),
521 "edit_image": ("Image", False),
522 "edit_object": ("Object", False),
523 "edit_text": ("Text", False),
524 "editable_bones": ("EditBone", True),
525 "fluid": ("FluidSimulationModifier", False),
526 "image_paint_object": ("Object", False),
527 "lamp": ("Lamp", False),
528 "lattice": ("Lattice", False),
529 "material": ("Material", False),
530 "material_slot": ("MaterialSlot", False),
531 "mesh": ("Mesh", False),
532 "meta_ball": ("MetaBall", False),
533 "object": ("Object", False),
534 "particle_edit_object": ("Object", False),
535 "particle_system": ("ParticleSystem", False),
536 "particle_system_editable": ("ParticleSystem", False),
537 "pose_bone": ("PoseBone", False),
538 "scene": ("Scene", False),
539 "sculpt_object": ("Object", False),
540 "selectable_bases": ("ObjectBase", True),
541 "selectable_objects": ("Object", True),
542 "selected_bases": ("ObjectBase", True),
543 "selected_bones": ("Bone", True),
544 "selected_editable_bases": ("ObjectBase", True),
545 "selected_editable_bones": ("Bone", True),
546 "selected_editable_objects": ("Object", True),
547 "selected_editable_sequences": ("Sequence", True),
548 "selected_nodes": ("Node", True),
549 "selected_objects": ("Object", True),
550 "selected_pose_bones": ("PoseBone", True),
551 "selected_sequences": ("Sequence", True),
552 "sequences": ("Sequence", True),
553 "smoke": ("SmokeModifier", False),
554 "soft_body": ("SoftBodyModifier", False),
555 "texture": ("Texture", False),
556 "texture_slot": ("MaterialTextureSlot", False),
557 "vertex_paint_object": ("Object", False),
558 "visible_bases": ("ObjectBase", True),
559 "visible_bones": ("Object", True),
560 "visible_objects": ("Object", True),
561 "visible_pose_bones": ("PoseBone", True),
562 "weight_paint_object": ("Object", False),
563 "world": ("World", False),
567 blend_cdll = ctypes.CDLL("")
568 for ctx_str in context_strings:
569 subsection = "%s Context" % ctx_str.split("_")[0].title()
570 fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * '-')))
572 attr = ctypes.addressof(getattr(blend_cdll, ctx_str))
573 c_char_p_p = ctypes.POINTER(ctypes.c_char_p)
574 char_array = c_char_p_p.from_address(attr)
576 while char_array[i] is not None:
577 member = ctypes.string_at(char_array[i]).decode()
578 fw(".. data:: %s\n\n" % member)
579 member_type, is_seq = type_map[member]
580 fw(" :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type))
584 # generate typemap...
585 # for member in sorted(unique):
586 # print(' "%s": ("", False),' % member)
587 if len(type_map) > len(unique):
588 raise Exception("Some types are not used: %s" % str([member for member in type_map if member not in unique]))
590 pass # will have raised an error above
595 def pyrna2sphinx(BASEPATH):
596 """ bpy.types and bpy.ops
598 structs, funcs, ops, props = rna_info.BuildRNAInfo()
599 if FILTER_BPY_TYPES is not None:
600 structs = {k: v for k, v in structs.items() if k[1] in FILTER_BPY_TYPES}
602 if FILTER_BPY_OPS is not None:
603 ops = {k: v for k, v in ops.items() if v.module_name in FILTER_BPY_OPS}
605 def write_param(ident, fw, prop, is_return=False):
609 kwargs = {"as_ret": True}
614 kwargs = {"as_arg": True}
615 identifier = " %s" % prop.identifier
617 kwargs["class_fmt"] = ":class:`%s`"
619 kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
621 type_descr = prop.get_type_description(**kwargs)
622 if prop.name or prop.description:
623 fw(ident + ":%s%s: %s\n" % (id_name, identifier, ", ".join(val for val in (prop.name, prop.description) if val)))
624 fw(ident + ":%s%s: %s\n" % (id_type, identifier, type_descr))
626 def write_struct(struct):
627 #if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
630 #if not struct.identifier == "Object":
633 filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % struct.identifier)
634 file = open(filepath, "w")
637 base_id = getattr(struct.base, "identifier", "")
641 base_id = _BPY_STRUCT_FAKE
644 title = "%s(%s)" % (struct.identifier, base_id)
646 title = struct.identifier
648 write_title(fw, title, "=")
650 fw(".. module:: bpy.types\n\n")
653 write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
655 base_ids = [base.identifier for base in struct.get_bases()]
658 base_ids.append(_BPY_STRUCT_FAKE)
663 if len(base_ids) > 1:
664 fw("base classes --- ")
666 fw("base class --- ")
668 fw(", ".join((":class:`%s`" % base_id) for base_id in base_ids))
671 subclass_ids = [s.identifier for s in structs.values() if s.base is struct if not rna_info.rna_id_ignore(s.identifier)]
673 fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in subclass_ids) + "\n\n")
675 base_id = getattr(struct.base, "identifier", "")
679 base_id = _BPY_STRUCT_FAKE
682 fw(".. class:: %s(%s)\n\n" % (struct.identifier, base_id))
684 fw(".. class:: %s\n\n" % struct.identifier)
686 fw(" %s\n\n" % struct.description)
688 # properties sorted in alphabetical order
689 sorted_struct_properties = struct.properties[:]
690 sorted_struct_properties.sort(key=lambda prop: prop.identifier)
692 for prop in sorted_struct_properties:
693 type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
694 # readonly properties use "data" directive, variables properties use "attribute" directive
695 if 'readonly' in type_descr:
696 fw(" .. data:: %s\n\n" % prop.identifier)
698 fw(" .. attribute:: %s\n\n" % prop.identifier)
700 fw(" %s\n\n" % prop.description)
701 fw(" :type: %s\n\n" % type_descr)
704 py_properties = struct.get_py_properties()
706 for identifier, py_prop in py_properties:
707 pyprop2sphinx(" ", fw, identifier, py_prop)
708 del py_properties, py_prop
710 for func in struct.functions:
711 args_str = ", ".join(prop.get_arg_default(force=False) for prop in func.args)
713 fw(" .. %s:: %s(%s)\n\n" % ("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
714 fw(" %s\n\n" % func.description)
716 for prop in func.args:
717 write_param(" ", fw, prop)
719 if len(func.return_values) == 1:
720 write_param(" ", fw, func.return_values[0], is_return=True)
721 elif func.return_values: # multiple return values
722 fw(" :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
723 for prop in func.return_values:
724 type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
725 descr = prop.description
728 fw(" `%s`, %s, %s\n\n" % (prop.identifier, descr, type_descr))
733 py_funcs = struct.get_py_functions()
736 for identifier, py_func in py_funcs:
737 pyfunc2sphinx(" ", fw, identifier, py_func, is_class=True)
738 del py_funcs, py_func
740 py_funcs = struct.get_py_c_functions()
743 for identifier, py_func in py_funcs:
744 py_c_func2sphinx(" ", fw, "bpy.types", struct.identifier, identifier, py_func, is_class=True)
748 if struct.base or _BPY_STRUCT_FAKE:
749 bases = list(reversed(struct.get_bases()))
755 descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
758 for key, descr in descr_items:
759 if type(descr) == GetSetDescriptorType:
760 lines.append(" * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
763 for prop in base.properties:
764 lines.append(" * :class:`%s.%s`\n" % (base.identifier, prop.identifier))
766 for identifier, py_prop in base.get_py_properties():
767 lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
769 for identifier, py_prop in base.get_py_properties():
770 lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
773 fw(".. rubric:: Inherited Properties\n\n")
776 fw(" :columns: 2\n\n")
786 for key, descr in descr_items:
787 if type(descr) == MethodDescriptorType:
788 lines.append(" * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
791 for func in base.functions:
792 lines.append(" * :class:`%s.%s`\n" % (base.identifier, func.identifier))
793 for identifier, py_func in base.get_py_functions():
794 lines.append(" * :class:`%s.%s`\n" % (base.identifier, identifier))
797 fw(".. rubric:: Inherited Functions\n\n")
800 fw(" :columns: 2\n\n")
808 if struct.references:
809 # use this otherwise it gets in the index for a normal heading.
810 fw(".. rubric:: References\n\n")
813 fw(" :columns: 2\n\n")
815 for ref in struct.references:
816 ref_split = ref.split(".")
817 if len(ref_split) > 2:
818 ref = ref_split[-2] + "." + ref_split[-1]
819 fw(" * :class:`%s`\n" % ref)
822 # docs last?, disable for now
823 # write_example_ref("", fw, "bpy.types.%s" % struct.identifier)
826 if "bpy.types" not in EXCLUDE_MODULES:
827 for struct in structs.values():
828 # TODO, rna_info should filter these out!
829 if "_OT_" in struct.identifier:
833 def fake_bpy_type(class_value, class_name, descr_str, use_subclasses=True):
834 filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % class_name)
835 file = open(filepath, "w")
838 write_title(fw, class_name, "=")
840 fw(".. module:: bpy.types\n")
844 subclass_ids = [s.identifier for s in structs.values() if s.base is None if not rna_info.rna_id_ignore(s.identifier)]
846 fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
848 fw(".. class:: %s\n\n" % class_name)
849 fw(" %s\n\n" % descr_str)
851 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)
853 descr_items = [(key, descr) for key, descr in sorted(class_value.__dict__.items()) if not key.startswith("__")]
855 for key, descr in descr_items:
856 if type(descr) == MethodDescriptorType: # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
857 py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
859 for key, descr in descr_items:
860 if type(descr) == GetSetDescriptorType:
861 py_descr2sphinx(" ", fw, descr, "bpy.types", class_name, key)
866 class_value = bpy.types.Struct.__bases__[0]
867 fake_bpy_type(class_value, _BPY_STRUCT_FAKE, "built-in base class for all classes in bpy.types.", use_subclasses=True)
869 if _BPY_PROP_COLLECTION_FAKE:
870 class_value = bpy.data.objects.__class__
871 fake_bpy_type(class_value, _BPY_PROP_COLLECTION_FAKE, "built-in class used for all collections.", use_subclasses=False)
875 API_BASEURL = "http://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts"
876 API_BASEURL_ADDON = "http://svn.blender.org/svnroot/bf-extensions/trunk/py/scripts"
877 API_BASEURL_ADDON_CONTRIB = "http://svn.blender.org/svnroot/bf-extensions/contrib/py/scripts"
880 for op in ops.values():
881 op_modules.setdefault(op.module_name, []).append(op)
884 for op_module_name, ops_mod in op_modules.items():
885 filepath = os.path.join(BASEPATH, "bpy.ops.%s.rst" % op_module_name)
886 file = open(filepath, "w")
889 title = "%s Operators" % op_module_name.replace("_", " ").title()
891 write_title(fw, title, "=")
893 fw(".. module:: bpy.ops.%s\n\n" % op_module_name)
895 ops_mod.sort(key=lambda op: op.func_name)
898 args_str = ", ".join(prop.get_arg_default(force=True) for prop in op.args)
899 fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str))
901 # if the description isn't valid, we output the standard warning
902 # with a link to the wiki so that people can help
903 if not op.description or op.description == "(undocumented operator)":
904 operator_description = undocumented_message('bpy.ops', op.module_name, op.func_name)
906 operator_description = op.description
908 fw(" %s\n\n" % operator_description)
910 write_param(" ", fw, prop)
914 location = op.get_location()
915 if location != (None, None):
916 if location[0].startswith("addons_contrib" + os.sep):
917 url_base = API_BASEURL_ADDON_CONTRIB
918 elif location[0].startswith("addons" + os.sep):
919 url_base = API_BASEURL_ADDON
921 url_base = API_BASEURL
923 fw(" :file: `%s <%s/%s>`_:%d\n\n" % (location[0], url_base, location[0], location[1]))
927 if "bpy.ops" not in EXCLUDE_MODULES:
931 def rna2sphinx(BASEPATH):
938 # conf.py - empty for now
939 filepath = os.path.join(BASEPATH, "conf.py")
940 file = open(filepath, "w")
943 version_string = ".".join(str(v) for v in bpy.app.version)
944 if bpy.app.build_revision != "Unknown":
945 version_string = version_string + " r" + bpy.app.build_revision
947 version_string_fp = "_".join(str(v) for v in bpy.app.version)
949 if bpy.app.version_cycle == "release":
950 version_string_pdf = "%s%s_release" % ("_".join(str(v) for v in bpy.app.version[:2]), bpy.app.version_char)
952 version_string_pdf = version_string_fp
954 fw("project = 'Blender'\n")
955 # fw("master_doc = 'index'\n")
956 fw("copyright = u'Blender Foundation'\n")
957 fw("version = '%s - API'\n" % version_string)
958 fw("release = '%s - API'\n" % version_string)
959 fw("html_theme = 'blender-org'\n")
960 fw("html_theme_path = ['../']\n")
961 fw("html_favicon = 'favicon.ico'\n")
962 # not helpful since the source us generated, adds to upload size.
963 fw("html_copy_source = False\n")
965 # needed for latex, pdf gen
966 fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n")
967 fw("latex_paper_size = 'a4paper'\n")
970 # main page needed for sphinx (index.html)
971 filepath = os.path.join(BASEPATH, "contents.rst")
972 file = open(filepath, "w")
975 fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
976 fw(" Blender Documentation contents\n")
977 fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
979 fw("Welcome, this document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date))
981 fw("`A PDF version of this document is also available <blender_python_reference_%s.pdf>`_\n" % version_string_pdf)
985 fw("============================\n")
986 fw("Blender/Python Documentation\n")
987 fw("============================\n")
990 fw("* `Quickstart Intro <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Intro>`_ if you are new to scripting in blender and want to get you're feet wet!\n")
991 fw("* `Blender/Python Overview <http://wiki.blender.org/index.php/Dev:2.5/Py/API/Overview>`_ for a more complete explanation of python integration in blender\n")
993 fw("===================\n")
994 fw("Application Modules\n")
995 fw("===================\n")
998 fw(" :maxdepth: 1\n\n")
999 if "bpy.context" not in EXCLUDE_MODULES:
1000 fw(" bpy.context.rst\n\n") # note: not actually a module
1001 if "bpy.data" not in EXCLUDE_MODULES:
1002 fw(" bpy.data.rst\n\n") # note: not actually a module
1003 if "bpy.ops" not in EXCLUDE_MODULES:
1004 fw(" bpy.ops.rst\n\n")
1005 if "bpy.types" not in EXCLUDE_MODULES:
1006 fw(" bpy.types.rst\n\n")
1009 if "bpy.utils" not in EXCLUDE_MODULES:
1010 fw(" bpy.utils.rst\n\n")
1011 if "bpy.path" not in EXCLUDE_MODULES:
1012 fw(" bpy.path.rst\n\n")
1013 if "bpy.app" not in EXCLUDE_MODULES:
1014 fw(" bpy.app.rst\n\n")
1017 if "bpy.props" not in EXCLUDE_MODULES:
1018 fw(" bpy.props.rst\n\n")
1020 fw("==================\n")
1021 fw("Standalone Modules\n")
1022 fw("==================\n")
1024 fw(".. toctree::\n")
1025 fw(" :maxdepth: 1\n\n")
1027 if "mathutils" not in EXCLUDE_MODULES:
1028 fw(" mathutils.rst\n\n")
1029 if "mathutils.geometry" not in EXCLUDE_MODULES:
1030 fw(" mathutils.geometry.rst\n\n")
1031 if "bgl" not in EXCLUDE_MODULES:
1033 if "blf" not in EXCLUDE_MODULES:
1035 if "aud" not in EXCLUDE_MODULES:
1037 if "bpy_extras" not in EXCLUDE_MODULES:
1038 fw(" bpy_extras.rst\n\n")
1041 if "bge" not in EXCLUDE_MODULES:
1042 fw("===================\n")
1043 fw("Game Engine Modules\n")
1044 fw("===================\n")
1046 fw(".. toctree::\n")
1047 fw(" :maxdepth: 1\n\n")
1048 fw(" bge.types.rst\n\n")
1049 fw(" bge.logic.rst\n\n")
1050 fw(" bge.render.rst\n\n")
1051 fw(" bge.events.rst\n\n")
1053 # rna generated change log
1058 fw(".. toctree::\n")
1059 fw(" :maxdepth: 1\n\n")
1060 fw(" change_log.rst\n\n")
1064 fw(".. note:: The Blender Python API has areas which are still in development.\n")
1066 fw(" The following areas are subject to change.\n")
1067 fw(" * operator behavior, names and arguments\n")
1068 fw(" * mesh creation and editing functions\n")
1070 fw(" These parts of the API are relatively stable and are unlikely to change significantly\n")
1071 fw(" * data API, access to attributes of blender data such as mesh verts, material color, timeline frames and scene objects\n")
1072 fw(" * user interface functions for defining buttons, creation of menus, headers, panels\n")
1073 fw(" * render engine integration\n")
1074 fw(" * modules: bgl, mathutils & game engine.\n")
1080 if "bpy.ops" not in EXCLUDE_MODULES:
1081 filepath = os.path.join(BASEPATH, "bpy.ops.rst")
1082 file = open(filepath, "w")
1084 fw("Operators (bpy.ops)\n")
1085 fw("===================\n\n")
1086 write_example_ref("", fw, "bpy.ops")
1087 fw(".. toctree::\n")
1089 fw(" bpy.ops.*\n\n")
1092 if "bpy.types" not in EXCLUDE_MODULES:
1093 filepath = os.path.join(BASEPATH, "bpy.types.rst")
1094 file = open(filepath, "w")
1096 fw("Types (bpy.types)\n")
1097 fw("=================\n\n")
1098 fw(".. toctree::\n")
1100 fw(" bpy.types.*\n\n")
1103 if "bpy.data" not in EXCLUDE_MODULES:
1104 # not actually a module, only write this file so we
1105 # can reference in the TOC
1106 filepath = os.path.join(BASEPATH, "bpy.data.rst")
1107 file = open(filepath, "w")
1109 fw("Data Access (bpy.data)\n")
1110 fw("======================\n\n")
1111 fw(".. module:: bpy\n")
1113 fw("This module is used for all blender/python access.\n")
1115 fw(".. data:: data\n")
1117 fw(" Access to blenders internal data\n")
1119 fw(" :type: :class:`bpy.types.BlendData`\n")
1121 fw(".. literalinclude:: ../examples/bpy.data.py\n")
1124 EXAMPLE_SET_USED.add("bpy.data")
1128 if "bpy.context" not in EXCLUDE_MODULES:
1129 # one of a kind, context doc (uses ctypes to extract info!)
1130 pycontext2sphinx(BASEPATH)
1133 if "bpy.utils" not in EXCLUDE_MODULES:
1134 from bpy import utils as module
1135 pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities")
1137 if "bpy.path" not in EXCLUDE_MODULES:
1138 from bpy import path as module
1139 pymodule2sphinx(BASEPATH, "bpy.path", module, "Path Utilities")
1141 if "bpy_extras" not in EXCLUDE_MODULES:
1142 import bpy_extras as module
1143 pymodule2sphinx(BASEPATH, "bpy_extras", module, "Extra Utilities")
1146 if "bpy.app" not in EXCLUDE_MODULES:
1147 from bpy import app as module
1148 pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data")
1150 if "bpy.props" not in EXCLUDE_MODULES:
1151 from bpy import props as module
1152 pymodule2sphinx(BASEPATH, "bpy.props", module, "Property Definitions")
1154 if "mathutils" not in EXCLUDE_MODULES:
1155 import mathutils as module
1156 pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities")
1158 if "mathutils.geometry" not in EXCLUDE_MODULES:
1159 import mathutils.geometry as module
1160 pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities")
1162 if "blf" not in EXCLUDE_MODULES:
1163 import blf as module
1164 pymodule2sphinx(BASEPATH, "blf", module, "Font Drawing")
1166 if "bgl" not in EXCLUDE_MODULES:
1167 #import bgl as module
1168 #pymodule2sphinx(BASEPATH, "bgl", module, "Blender OpenGl wrapper")
1171 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bgl.rst"), BASEPATH)
1173 if "aud" not in EXCLUDE_MODULES:
1174 import aud as module
1175 pymodule2sphinx(BASEPATH, "aud", module, "Audio System")
1180 # copy2 keeps time/date stamps
1181 if "bge" not in EXCLUDE_MODULES:
1182 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.types.rst"), BASEPATH)
1183 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.logic.rst"), BASEPATH)
1184 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.render.rst"), BASEPATH)
1185 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.events.rst"), BASEPATH)
1187 shutil.copy2(os.path.join(BASEPATH, "..", "rst", "change_log.rst"), BASEPATH)
1190 filepath = os.path.join(BASEPATH, "bpy.rst")
1191 file = open(filepath, "w")
1196 title = ":mod:`bpy` --- Blender Python Module"
1198 write_title(fw, title, "=")
1200 fw(".. module:: bpy.types\n\n")
1203 # bpy.types and bpy.ops
1204 pyrna2sphinx(BASEPATH)
1212 script_dir = os.path.dirname(__file__)
1213 path_in = os.path.join(script_dir, "sphinx-in")
1214 path_out = os.path.join(script_dir, "sphinx-out")
1215 path_examples = os.path.join(script_dir, "examples")
1216 # only for partial updates
1217 path_in_tmp = path_in + "-tmp"
1219 if not os.path.exists(path_in):
1222 for f in os.listdir(path_examples):
1223 if f.endswith(".py"):
1224 EXAMPLE_SET.add(os.path.splitext(f)[0])
1226 # only for full updates
1227 if _BPY_FULL_REBUILD:
1228 shutil.rmtree(path_in, True)
1229 shutil.rmtree(path_out, True)
1231 # write here, then move
1232 shutil.rmtree(path_in_tmp, True)
1234 rna2sphinx(path_in_tmp)
1236 if not _BPY_FULL_REBUILD:
1239 # now move changed files from 'path_in_tmp' --> 'path_in'
1240 file_list_path_in = set(os.listdir(path_in))
1241 file_list_path_in_tmp = set(os.listdir(path_in_tmp))
1243 # remove deprecated files that have been removed.
1244 for f in sorted(file_list_path_in):
1245 if f not in file_list_path_in_tmp:
1246 print("\tdeprecated: %s" % f)
1247 os.remove(os.path.join(path_in, f))
1249 # freshen with new files.
1250 for f in sorted(file_list_path_in_tmp):
1251 f_from = os.path.join(path_in_tmp, f)
1252 f_to = os.path.join(path_in, f)
1255 if f in file_list_path_in:
1256 if filecmp.cmp(f_from, f_to):
1260 print("\tupdating: %s" % f)
1261 shutil.copy(f_from, f_to)
1263 print("\tkeeping: %s" % f) # eh, not that useful'''
1265 EXAMPLE_SET_UNUSED = EXAMPLE_SET - EXAMPLE_SET_USED
1266 if EXAMPLE_SET_UNUSED:
1267 print("\nUnused examples found in '%s'..." % path_examples)
1268 for f in EXAMPLE_SET_UNUSED:
1270 print(" %d total\n" % len(EXAMPLE_SET_UNUSED))
1275 if __name__ == '__main__':