Merged changes in the trunk up to revision 44039.
[blender-staging.git] / doc / python_api / sphinx_doc_gen.py
1  # ***** BEGIN GPL LICENSE BLOCK *****
2  #
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.
7  #
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.
12  #
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.
16  #
17  # Contributor(s): Campbell Barton
18  #
19  # #**** END GPL LICENSE BLOCK #****
20
21 # <pep8 compliant>
22
23 script_help_msg = '''
24 Usage:
25
26 For HTML generation
27 -------------------
28 - Run this script from blenders root path once you have compiled blender
29
30     ./blender.bin --background -noaudio --python doc/python_api/sphinx_doc_gen.py
31
32   This will generate python files in doc/python_api/sphinx-in/
33   providing ./blender.bin is or links to the blender executable
34
35 - Generate html docs by running...
36
37     cd doc/python_api
38     sphinx-build sphinx-in sphinx-out
39
40   This requires sphinx 1.0.7 to be installed.
41
42 For PDF generation
43 ------------------
44 - After you have built doc/python_api/sphinx-in (see above), run:
45
46     sphinx-build -b latex doc/python_api/sphinx-in doc/python_api/sphinx-out
47     cd doc/python_api/sphinx-out
48     make
49 '''
50
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 blender")
54     print(script_help_msg)
55
56     import sys
57     sys.exit()
58
59
60 # Switch for quick testing
61 if 1:
62     # full build
63     EXCLUDE_INFO_DOCS = False
64     EXCLUDE_MODULES = ()
65     FILTER_BPY_TYPES = None
66     FILTER_BPY_OPS = None
67
68 else:
69     EXCLUDE_INFO_DOCS = False
70     # for testing so doc-builds dont take so long.
71     EXCLUDE_MODULES = (
72         "bpy.context",
73         #"bpy.app",
74         #"bpy.app.handlers",
75         "bpy.path",
76         "bpy.data",
77         "bpy.props",
78         "bpy.utils",
79         "bpy.context",
80         "bpy.types",  # supports filtering
81         "bpy.ops",  # supports filtering
82         "bpy_extras",
83         "bge",
84         "aud",
85         "bgl",
86         "blf",
87         "gpu",
88         "mathutils",
89         "mathutils.geometry",
90         "mathutils.noise",
91         "Freestyle",
92     )
93
94     FILTER_BPY_TYPES = ("bpy_struct", "Operator", "ID")  # allow
95     FILTER_BPY_OPS = ("import.scene", )  # allow
96
97     # for quick rebuilds
98     """
99 rm -rf /b/doc/python_api/sphinx-* && \
100 ./blender.bin --background -noaudio --factory-startup --python  doc/python_api/sphinx_doc_gen.py && \
101 sphinx-build doc/python_api/sphinx-in doc/python_api/sphinx-out
102
103     """
104
105 # extra info, not api reference docs
106 # stored in ./rst/info/
107 INFO_DOCS = (
108     ("info_quickstart.rst", "Blender/Python Quickstart: new to blender/scripting and want to get your feet wet?"),
109     ("info_overview.rst", "Blender/Python API Overview: a more complete explanation of python integration"),
110     ("info_best_practice.rst", "Best Practice: Conventions to follow for writing good scripts"),
111     ("info_tips_and_tricks.rst", "Tips and Tricks: Hints to help you while writeing scripts for blender"),
112     ("info_gotcha.rst", "Gotcha's: some of the problems you may come up against when writing scripts"),
113     )
114
115 # only support for properties atm.
116 RNA_BLACKLIST = {
117     # messes up PDF!, really a bug but for now just workaround.
118     "UserPreferencesSystem": {"language", },
119     }
120
121
122 # -----------------------------------------------------------------------------
123 # configure compile time options
124
125 try:
126     __import__("aud")
127 except ImportError:
128     print("Warning: Built without 'aud' module, docs incomplete...")
129     EXCLUDE_MODULES = EXCLUDE_MODULES + ("aud", )
130
131
132 # import rpdb2; rpdb2.start_embedded_debugger('test')
133 import os
134 import inspect
135 import bpy
136 import rna_info
137
138 # lame, python wont give some access
139 ClassMethodDescriptorType = type(dict.__dict__['fromkeys'])
140 MethodDescriptorType = type(dict.get)
141 GetSetDescriptorType = type(int.real)
142 StaticMethodType = type(staticmethod(lambda: None))
143 from types import MemberDescriptorType
144
145 EXAMPLE_SET = set()
146 EXAMPLE_SET_USED = set()
147
148 _BPY_STRUCT_FAKE = "bpy_struct"
149 _BPY_PROP_COLLECTION_FAKE = "bpy_prop_collection"
150 _BPY_FULL_REBUILD = False
151
152 if _BPY_PROP_COLLECTION_FAKE:
153     _BPY_PROP_COLLECTION_ID = ":class:`%s`" % _BPY_PROP_COLLECTION_FAKE
154 else:
155     _BPY_PROP_COLLECTION_ID = "collection"
156
157
158 def is_struct_seq(value):
159     return isinstance(value, tuple) and type(tuple) != tuple and hasattr(value, "n_fields")
160
161
162 def undocumented_message(module_name, type_name, identifier):
163     if str(type_name).startswith('<module'):
164         preloadtitle = '%s.%s' % (module_name, identifier)
165     else:
166         preloadtitle = '%s.%s.%s' % (module_name, type_name, identifier)
167     message = "Undocumented (`contribute "\
168         "<http://wiki.blender.org/index.php/Dev:2.5/Py/API/Documentation/Contribute"\
169         "?action=edit&section=new&preload=Dev:2.5/Py/API/Documentation/Contribute/Howto-message"\
170         "&preloadtitle=%s>`_)\n\n" % preloadtitle
171     return message
172
173
174 def range_str(val):
175     '''
176     Converts values to strings for the range directive.
177     (unused function it seems)
178     '''
179     if val < -10000000:
180         return '-inf'
181     elif val > 10000000:
182         return 'inf'
183     elif type(val) == float:
184         return '%g' % val
185     else:
186         return str(val)
187
188
189 def example_extract_docstring(filepath):
190     file = open(filepath, "r", encoding="utf-8")
191     line = file.readline()
192     line_no = 0
193     text = []
194     if line.startswith('"""'):  # assume nothing here
195         line_no += 1
196     else:
197         file.close()
198         return "", 0
199
200     for line in file.readlines():
201         line_no += 1
202         if line.startswith('"""'):
203             break
204         else:
205             text.append(line.rstrip())
206
207     line_no += 1
208     file.close()
209     return "\n".join(text), line_no
210
211
212 def write_title(fw, text, heading_char):
213     fw("%s\n%s\n\n" % (text, len(text) * heading_char))
214
215
216 def write_example_ref(ident, fw, example_id, ext="py"):
217     if example_id in EXAMPLE_SET:
218
219         # extract the comment
220         filepath = "../examples/%s.%s" % (example_id, ext)
221         filepath_full = os.path.join(os.path.dirname(fw.__self__.name), filepath)
222
223         text, line_no = example_extract_docstring(filepath_full)
224
225         for line in text.split("\n"):
226             fw("%s\n" % (ident + line).rstrip())
227         fw("\n")
228
229         fw("%s.. literalinclude:: %s\n" % (ident, filepath))
230         if line_no > 0:
231             fw("%s   :lines: %d-\n" % (ident, line_no))
232         fw("\n")
233         EXAMPLE_SET_USED.add(example_id)
234     else:
235         if bpy.app.debug:
236             print("\tskipping example:", example_id)
237
238     # Support for numbered files bpy.types.Operator -> bpy.types.Operator.1.py
239     i = 1
240     while True:
241         example_id_num = "%s.%d" % (example_id, i)
242         if example_id_num in EXAMPLE_SET:
243             write_example_ref(ident, fw, example_id_num, ext)
244             i += 1
245         else:
246             break
247
248
249 def write_indented_lines(ident, fn, text, strip=True):
250     '''
251     Apply same indentation to all lines in a multilines text.
252     '''
253     if text is None:
254         return
255
256     lines = text.split("\n")
257
258     # strip empty lines from the start/end
259     while lines and not lines[0].strip():
260         del lines[0]
261     while lines and not lines[-1].strip():
262         del lines[-1]
263
264     if strip:
265         ident_strip = 1000
266         for l in lines:
267             if l.strip():
268                 ident_strip = min(ident_strip, len(l) - len(l.lstrip()))
269         for l in lines:
270             fn(ident + l[ident_strip:] + "\n")
271     else:
272         for l in lines:
273             fn(ident + l + "\n")
274
275
276 def pymethod2sphinx(ident, fw, identifier, py_func):
277     '''
278     class method to sphinx
279     '''
280     arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
281     if arg_str.startswith("(self, "):
282         arg_str = "(" + arg_str[7:]
283         func_type = "method"
284     elif arg_str.startswith("(cls, "):
285         arg_str = "(" + arg_str[6:]
286         func_type = "classmethod"
287     else:
288         func_type = "staticmethod"
289
290     fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
291     if py_func.__doc__:
292         write_indented_lines(ident + "   ", fw, py_func.__doc__)
293         fw("\n")
294
295
296 def pyfunc2sphinx(ident, fw, identifier, py_func, is_class=True):
297     '''
298     function or class method to sphinx
299     '''
300     arg_str = inspect.formatargspec(*inspect.getargspec(py_func))
301
302     if not is_class:
303         func_type = "function"
304
305         # ther rest are class methods
306     elif arg_str.startswith("(self, "):
307         arg_str = "(" + arg_str[7:]
308         func_type = "method"
309     elif arg_str.startswith("(cls, "):
310         arg_str = "(" + arg_str[6:]
311         func_type = "classmethod"
312     else:
313         func_type = "staticmethod"
314
315     fw(ident + ".. %s:: %s%s\n\n" % (func_type, identifier, arg_str))
316     if py_func.__doc__:
317         write_indented_lines(ident + "   ", fw, py_func.__doc__)
318         fw("\n")
319
320
321 def py_descr2sphinx(ident, fw, descr, module_name, type_name, identifier):
322     if identifier.startswith("_"):
323         return
324
325     doc = descr.__doc__
326     if not doc:
327         doc = undocumented_message(module_name, type_name, identifier)
328
329     if type(descr) == GetSetDescriptorType:
330         fw(ident + ".. attribute:: %s\n\n" % identifier)
331         write_indented_lines(ident + "   ", fw, doc, False)
332         fw("\n")
333     elif type(descr) == MemberDescriptorType:  # same as above but use 'data'
334         fw(ident + ".. data:: %s\n\n" % identifier)
335         write_indented_lines(ident + "   ", fw, doc, False)
336         fw("\n")
337     elif type(descr) in (MethodDescriptorType, ClassMethodDescriptorType):
338         write_indented_lines(ident, fw, doc, False)
339         fw("\n")
340     else:
341         raise TypeError("type was not GetSetDescriptorType, MethodDescriptorType or ClassMethodDescriptorType")
342
343     write_example_ref(ident + "   ", fw, module_name + "." + type_name + "." + identifier)
344     fw("\n")
345
346
347 def py_c_func2sphinx(ident, fw, module_name, type_name, identifier, py_func, is_class=True):
348     '''
349     c defined function to sphinx.
350     '''
351
352     # dump the docstring, assume its formatted correctly
353     if py_func.__doc__:
354         write_indented_lines(ident, fw, py_func.__doc__, False)
355         fw("\n")
356     else:
357         fw(ident + ".. function:: %s()\n\n" % identifier)
358         fw(ident + "   " + undocumented_message(module_name, type_name, identifier))
359
360     if is_class:
361         write_example_ref(ident + "   ", fw, module_name + "." + type_name + "." + identifier)
362     else:
363         write_example_ref(ident + "   ", fw, module_name + "." + identifier)
364
365     fw("\n")
366
367
368 def pyprop2sphinx(ident, fw, identifier, py_prop):
369     '''
370     python property to sphinx
371     '''
372     # readonly properties use "data" directive, variables use "attribute" directive
373     if py_prop.fset is None:
374         fw(ident + ".. data:: %s\n\n" % identifier)
375     else:
376         fw(ident + ".. attribute:: %s\n\n" % identifier)
377     write_indented_lines(ident + "   ", fw, py_prop.__doc__)
378     if py_prop.fset is None:
379         fw(ident + "   (readonly)\n\n")
380
381
382 def pymodule2sphinx(BASEPATH, module_name, module, title):
383     import types
384     attribute_set = set()
385     filepath = os.path.join(BASEPATH, module_name + ".rst")
386
387     module_all = getattr(module, "__all__", None)
388     module_dir = sorted(dir(module))
389
390     if module_all:
391         module_dir = module_all
392
393     file = open(filepath, "w", encoding="utf-8")
394
395     fw = file.write
396
397     write_title(fw, "%s (%s)" % (title, module_name), "=")
398
399     fw(".. module:: %s\n\n" % module_name)
400
401     if module.__doc__:
402         # Note, may contain sphinx syntax, dont mangle!
403         fw(module.__doc__.strip())
404         fw("\n\n")
405
406     write_example_ref("", fw, module_name)
407
408     # write submodules
409     # we could also scan files but this ensures __all__ is used correctly
410     if module_all is not None:
411         submod_name = None
412         submod = None
413         submod_ls = []
414         for submod_name in module_all:
415             ns = {}
416             exec_str = "from %s import %s as submod" % (module.__name__, submod_name)
417             exec(exec_str, ns, ns)
418             submod = ns["submod"]
419             if type(submod) == types.ModuleType:
420                 submod_ls.append((submod_name, submod))
421
422         del submod_name
423         del submod
424
425         if submod_ls:
426             fw(".. toctree::\n")
427             fw("   :maxdepth: 1\n\n")
428
429             for submod_name, submod in submod_ls:
430                 submod_name_full = "%s.%s" % (module_name, submod_name)
431                 fw("   %s.rst\n\n" % submod_name_full)
432
433                 pymodule2sphinx(BASEPATH, submod_name_full, submod, "%s submodule" % module_name)
434         del submod_ls
435     # done writing submodules!
436
437     # write members of the module
438     # only tested with PyStructs which are not exactly modules
439     for key, descr in sorted(type(module).__dict__.items()):
440         if key.startswith("__"):
441             continue
442         # naughty, we also add getset's into PyStructs, this is not typical py but also not incorrect.
443
444         # type_name is only used for examples and messages
445         type_name = str(type(module)).strip("<>").split(" ", 1)[-1][1:-1]  # "<class 'bpy.app.handlers'>" --> bpy.app.handlers
446         if type(descr) == types.GetSetDescriptorType:
447             py_descr2sphinx("", fw, descr, module_name, type_name, key)
448             attribute_set.add(key)
449     descr_sorted = []
450     for key, descr in sorted(type(module).__dict__.items()):
451         if key.startswith("__"):
452             continue
453
454         if type(descr) == MemberDescriptorType:
455             if descr.__doc__:
456                 value = getattr(module, key, None)
457
458                 value_type = type(value)
459                 descr_sorted.append((key, descr, value, type(value)))
460     # sort by the valye type
461     descr_sorted.sort(key=lambda descr_data: str(descr_data[3]))
462     for key, descr, value, value_type in descr_sorted:
463
464         # must be documented as a submodule
465         if is_struct_seq(value):
466             continue
467
468         type_name = value_type.__name__
469         py_descr2sphinx("", fw, descr, module_name, type_name, key)
470
471         attribute_set.add(key)
472
473     del key, descr, descr_sorted
474
475     classes = []
476     submodules = []
477
478     # use this list so we can sort by type
479     module_dir_value_type = []
480
481     for attribute in module_dir:
482         if attribute.startswith("_"):
483             continue
484
485         if attribute in attribute_set:
486             continue
487
488         if attribute.startswith("n_"):  # annoying exception, needed for bpy.app
489             continue
490
491         # workaround for bpy.app documenting .index() and .count()
492         if isinstance(module, tuple) and hasattr(tuple, attribute):
493             continue
494
495         value = getattr(module, attribute)
496
497         module_dir_value_type.append((attribute, value, type(value)))
498
499     # sort by str of each type
500     # this way lists, functions etc are grouped.
501     module_dir_value_type.sort(key=lambda triple: str(triple[2]))
502
503     for attribute, value, value_type in module_dir_value_type:
504         if value_type == types.FunctionType:
505             pyfunc2sphinx("", fw, attribute, value, is_class=False)
506         elif value_type in (types.BuiltinMethodType, types.BuiltinFunctionType):  # both the same at the moment but to be future proof
507             # note: can't get args from these, so dump the string as is
508             # this means any module used like this must have fully formatted docstrings.
509             py_c_func2sphinx("", fw, module_name, None, attribute, value, is_class=False)
510         elif value_type == type:
511             classes.append((attribute, value))
512         elif issubclass(value_type, types.ModuleType):
513             submodules.append((attribute, value))
514         elif value_type in (bool, int, float, str, tuple):
515             # constant, not much fun we can do here except to list it.
516             # TODO, figure out some way to document these!
517             #fw(".. data:: %s\n\n" % attribute)
518             write_indented_lines("   ", fw, "constant value %s" % repr(value), False)
519             fw("\n")
520         else:
521             print("\tnot documenting %s.%s of %r type" % (module_name, attribute, value_type.__name__))
522             continue
523
524         attribute_set.add(attribute)
525         # TODO, more types...
526     del module_dir_value_type
527
528     # TODO, bpy_extras does this already, mathutils not.
529     """
530     if submodules:
531         fw("\n"
532            "**********\n"
533            "Submodules\n"
534            "**********\n"
535            "\n"
536            )
537         for attribute, submod in submodules:
538             fw("* :mod:`%s.%s`\n" % (module_name, attribute))
539         fw("\n")
540     """
541
542     # write collected classes now
543     for (type_name, value) in classes:
544         # May need to be its own function
545         fw(".. class:: %s\n\n" % type_name)
546         if value.__doc__:
547             write_indented_lines("   ", fw, value.__doc__, False)
548             fw("\n")
549         write_example_ref("   ", fw, module_name + "." + type_name)
550
551         descr_items = [(key, descr) for key, descr in sorted(value.__dict__.items()) if not key.startswith("__")]
552
553         for key, descr in descr_items:
554             if type(descr) == ClassMethodDescriptorType:
555                 py_descr2sphinx("   ", fw, descr, module_name, type_name, key)
556
557         for key, descr in descr_items:
558             if type(descr) == MethodDescriptorType:
559                 py_descr2sphinx("   ", fw, descr, module_name, type_name, key)
560
561         for key, descr in descr_items:
562             if type(descr) == GetSetDescriptorType:
563                 py_descr2sphinx("   ", fw, descr, module_name, type_name, key)
564
565         for key, descr in descr_items:
566             if type(descr) == StaticMethodType:
567                 descr = getattr(value, key)
568                 write_indented_lines("   ", fw, descr.__doc__ or "Undocumented", False)
569                 fw("\n")
570
571         fw("\n\n")
572
573     file.close()
574
575
576 def pycontext2sphinx(BASEPATH):
577     # Only use once. very irregular
578
579     filepath = os.path.join(BASEPATH, "bpy.context.rst")
580     file = open(filepath, "w", encoding="utf-8")
581     fw = file.write
582     fw("Context Access (bpy.context)\n")
583     fw("============================\n\n")
584     fw(".. module:: bpy.context\n")
585     fw("\n")
586     fw("The context members available depend on the area of blender which is currently being accessed.\n")
587     fw("\n")
588     fw("Note that all context values are readonly, but may be modified through the data api or by running operators\n\n")
589
590     # nasty, get strings directly from blender because there is no other way to get it
591     import ctypes
592
593     context_strings = (
594         "screen_context_dir",
595         "view3d_context_dir",
596         "buttons_context_dir",
597         "image_context_dir",
598         "node_context_dir",
599         "text_context_dir",
600     )
601
602     # Changes in blender will force errors here
603     type_map = {
604         "active_base": ("ObjectBase", False),
605         "active_bone": ("Bone", False),
606         "active_object": ("Object", False),
607         "active_operator": ("Operator", False),
608         "active_pose_bone": ("PoseBone", False),
609         "armature": ("Armature", False),
610         "bone": ("Bone", False),
611         "brush": ("Brush", False),
612         "camera": ("Camera", False),
613         "cloth": ("ClothModifier", False),
614         "collision": ("CollisionModifier", False),
615         "curve": ("Curve", False),
616         "dynamic_paint": ("DynamicPaintModifier", False),
617         "edit_bone": ("EditBone", False),
618         "edit_image": ("Image", False),
619         "edit_object": ("Object", False),
620         "edit_text": ("Text", False),
621         "editable_bones": ("EditBone", True),
622         "fluid": ("FluidSimulationModifier", False),
623         "image_paint_object": ("Object", False),
624         "lamp": ("Lamp", False),
625         "lattice": ("Lattice", False),
626         "material": ("Material", False),
627         "material_slot": ("MaterialSlot", False),
628         "mesh": ("Mesh", False),
629         "meta_ball": ("MetaBall", False),
630         "object": ("Object", False),
631         "particle_edit_object": ("Object", False),
632         "particle_system": ("ParticleSystem", False),
633         "particle_system_editable": ("ParticleSystem", False),
634         "pose_bone": ("PoseBone", False),
635         "scene": ("Scene", False),
636         "sculpt_object": ("Object", False),
637         "selectable_bases": ("ObjectBase", True),
638         "selectable_objects": ("Object", True),
639         "selected_bases": ("ObjectBase", True),
640         "selected_bones": ("Bone", True),
641         "selected_editable_bases": ("ObjectBase", True),
642         "selected_editable_bones": ("Bone", True),
643         "selected_editable_objects": ("Object", True),
644         "selected_editable_sequences": ("Sequence", True),
645         "selected_nodes": ("Node", True),
646         "selected_objects": ("Object", True),
647         "selected_pose_bones": ("PoseBone", True),
648         "selected_sequences": ("Sequence", True),
649         "sequences": ("Sequence", True),
650         "smoke": ("SmokeModifier", False),
651         "soft_body": ("SoftBodyModifier", False),
652         "speaker": ("Speaker", False),
653         "texture": ("Texture", False),
654         "texture_slot": ("MaterialTextureSlot", False),
655         "texture_user": ("ID", False),
656         "vertex_paint_object": ("Object", False),
657         "visible_bases": ("ObjectBase", True),
658         "visible_bones": ("Object", True),
659         "visible_objects": ("Object", True),
660         "visible_pose_bones": ("PoseBone", True),
661         "weight_paint_object": ("Object", False),
662         "world": ("World", False),
663     }
664
665     unique = set()
666     blend_cdll = ctypes.CDLL("")
667     for ctx_str in context_strings:
668         subsection = "%s Context" % ctx_str.split("_")[0].title()
669         fw("\n%s\n%s\n\n" % (subsection, (len(subsection) * '-')))
670
671         attr = ctypes.addressof(getattr(blend_cdll, ctx_str))
672         c_char_p_p = ctypes.POINTER(ctypes.c_char_p)
673         char_array = c_char_p_p.from_address(attr)
674         i = 0
675         while char_array[i] is not None:
676             member = ctypes.string_at(char_array[i]).decode(encoding="ascii")
677             fw(".. data:: %s\n\n" % member)
678             member_type, is_seq = type_map[member]
679             fw("   :type: %s :class:`bpy.types.%s`\n\n" % ("sequence of " if is_seq else "", member_type))
680             unique.add(member)
681             i += 1
682
683     # generate typemap...
684     # for member in sorted(unique):
685     #     print('        "%s": ("", False),' % member)
686     if len(type_map) > len(unique):
687         raise Exception("Some types are not used: %s" % str([member for member in type_map if member not in unique]))
688     else:
689         pass  # will have raised an error above
690
691     file.close()
692
693
694 def pyrna_enum2sphinx(prop, use_empty_descriptions=False):
695     """ write a bullet point list of enum + descrptons
696     """
697
698     if use_empty_descriptions:
699         ok = True
700     else:
701         ok = False
702         for identifier, name, description in prop.enum_items:
703             if description:
704                 ok = True
705                 break
706
707     if ok:
708         return "".join(["* ``%s`` %s.\n" %
709                         (identifier,
710                          ", ".join(val for val in (name, description) if val),
711                          )
712                         for identifier, name, description in prop.enum_items
713                         ])
714     else:
715         return ""
716
717
718 def pyrna2sphinx(BASEPATH):
719     """ bpy.types and bpy.ops
720     """
721     structs, funcs, ops, props = rna_info.BuildRNAInfo()
722     if FILTER_BPY_TYPES is not None:
723         structs = {k: v for k, v in structs.items() if k[1] in FILTER_BPY_TYPES}
724
725     if FILTER_BPY_OPS is not None:
726         ops = {k: v for k, v in ops.items() if v.module_name in FILTER_BPY_OPS}
727
728     def write_param(ident, fw, prop, is_return=False):
729         if is_return:
730             id_name = "return"
731             id_type = "rtype"
732             kwargs = {"as_ret": True}
733             identifier = ""
734         else:
735             id_name = "arg"
736             id_type = "type"
737             kwargs = {"as_arg": True}
738             identifier = " %s" % prop.identifier
739
740         kwargs["class_fmt"] = ":class:`%s`"
741
742         kwargs["collection_id"] = _BPY_PROP_COLLECTION_ID
743
744         type_descr = prop.get_type_description(**kwargs)
745
746         enum_text = pyrna_enum2sphinx(prop)
747
748         if prop.name or prop.description or enum_text:
749             fw(ident + ":%s%s:\n\n" % (id_name, identifier))
750
751             if prop.name or prop.description:
752                 fw(ident + "   " + ", ".join(val for val in (prop.name, prop.description) if val) + "\n\n")
753
754             # special exception, cant use genric code here for enums
755             if enum_text:
756                 write_indented_lines(ident + "   ", fw, enum_text)
757                 fw("\n")
758             del enum_text
759             # end enum exception
760
761         fw(ident + ":%s%s: %s\n" % (id_type, identifier, type_descr))
762
763     def write_struct(struct):
764         #if not struct.identifier.startswith("Sc") and not struct.identifier.startswith("I"):
765         #    return
766
767         #if not struct.identifier == "Object":
768         #    return
769
770         filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % struct.identifier)
771         file = open(filepath, "w", encoding="utf-8")
772         fw = file.write
773
774         base_id = getattr(struct.base, "identifier", "")
775         struct_id = struct.identifier
776
777         if _BPY_STRUCT_FAKE:
778             if not base_id:
779                 base_id = _BPY_STRUCT_FAKE
780
781         if base_id:
782             title = "%s(%s)" % (struct_id, base_id)
783         else:
784             title = struct_id
785
786         write_title(fw, title, "=")
787
788         fw(".. module:: bpy.types\n\n")
789
790         # docs first?, ok
791         write_example_ref("", fw, "bpy.types.%s" % struct_id)
792
793         base_ids = [base.identifier for base in struct.get_bases()]
794
795         if _BPY_STRUCT_FAKE:
796             base_ids.append(_BPY_STRUCT_FAKE)
797
798         base_ids.reverse()
799
800         if base_ids:
801             if len(base_ids) > 1:
802                 fw("base classes --- ")
803             else:
804                 fw("base class --- ")
805
806             fw(", ".join((":class:`%s`" % base_id) for base_id in base_ids))
807             fw("\n\n")
808
809         subclass_ids = [s.identifier for s in structs.values() if s.base is struct if not rna_info.rna_id_ignore(s.identifier)]
810         if subclass_ids:
811             fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in subclass_ids) + "\n\n")
812
813         base_id = getattr(struct.base, "identifier", "")
814
815         if _BPY_STRUCT_FAKE:
816             if not base_id:
817                 base_id = _BPY_STRUCT_FAKE
818
819         if base_id:
820             fw(".. class:: %s(%s)\n\n" % (struct_id, base_id))
821         else:
822             fw(".. class:: %s\n\n" % struct_id)
823
824         fw("   %s\n\n" % struct.description)
825
826         # properties sorted in alphabetical order
827         sorted_struct_properties = struct.properties[:]
828         sorted_struct_properties.sort(key=lambda prop: prop.identifier)
829
830         # support blacklisting props
831         struct_blacklist = RNA_BLACKLIST.get(struct_id, ())
832
833         for prop in sorted_struct_properties:
834
835             # support blacklisting props
836             if prop.identifier in struct_blacklist:
837                 continue
838
839             type_descr = prop.get_type_description(class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
840             # readonly properties use "data" directive, variables properties use "attribute" directive
841             if 'readonly' in type_descr:
842                 fw("   .. data:: %s\n\n" % prop.identifier)
843             else:
844                 fw("   .. attribute:: %s\n\n" % prop.identifier)
845             if prop.description:
846                 fw("      %s\n\n" % prop.description)
847
848             # special exception, cant use genric code here for enums
849             if prop.type == "enum":
850                 enum_text = pyrna_enum2sphinx(prop)
851                 if enum_text:
852                     write_indented_lines("      ", fw, enum_text)
853                     fw("\n")
854                 del enum_text
855             # end enum exception
856
857             fw("      :type: %s\n\n" % type_descr)
858
859         # python attributes
860         py_properties = struct.get_py_properties()
861         py_prop = None
862         for identifier, py_prop in py_properties:
863             pyprop2sphinx("   ", fw, identifier, py_prop)
864         del py_properties, py_prop
865
866         for func in struct.functions:
867             args_str = ", ".join(prop.get_arg_default(force=False) for prop in func.args)
868
869             fw("   .. %s:: %s(%s)\n\n" % ("classmethod" if func.is_classmethod else "method", func.identifier, args_str))
870             fw("      %s\n\n" % func.description)
871
872             for prop in func.args:
873                 write_param("      ", fw, prop)
874
875             if len(func.return_values) == 1:
876                 write_param("      ", fw, func.return_values[0], is_return=True)
877             elif func.return_values:  # multiple return values
878                 fw("      :return (%s):\n" % ", ".join(prop.identifier for prop in func.return_values))
879                 for prop in func.return_values:
880                     # TODO, pyrna_enum2sphinx for multiple return values... actually dont think we even use this but still!!!
881                     type_descr = prop.get_type_description(as_ret=True, class_fmt=":class:`%s`", collection_id=_BPY_PROP_COLLECTION_ID)
882                     descr = prop.description
883                     if not descr:
884                         descr = prop.name
885                     fw("         `%s`, %s, %s\n\n" % (prop.identifier, descr, type_descr))
886
887             write_example_ref("      ", fw, "bpy.types." + struct_id + "." + func.identifier)
888
889             fw("\n")
890
891         # python methods
892         py_funcs = struct.get_py_functions()
893         py_func = None
894
895         for identifier, py_func in py_funcs:
896             pyfunc2sphinx("   ", fw, identifier, py_func, is_class=True)
897         del py_funcs, py_func
898
899         py_funcs = struct.get_py_c_functions()
900         py_func = None
901
902         for identifier, py_func in py_funcs:
903             py_c_func2sphinx("   ", fw, "bpy.types", struct_id, identifier, py_func, is_class=True)
904
905         lines = []
906
907         if struct.base or _BPY_STRUCT_FAKE:
908             bases = list(reversed(struct.get_bases()))
909
910             # props
911             lines[:] = []
912
913             if _BPY_STRUCT_FAKE:
914                 descr_items = [(key, descr) for key, descr in sorted(bpy.types.Struct.__bases__[0].__dict__.items()) if not key.startswith("__")]
915
916             if _BPY_STRUCT_FAKE:
917                 for key, descr in descr_items:
918                     if type(descr) == GetSetDescriptorType:
919                         lines.append("   * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
920
921             for base in bases:
922                 for prop in base.properties:
923                     lines.append("   * :class:`%s.%s`\n" % (base.identifier, prop.identifier))
924
925                 for identifier, py_prop in base.get_py_properties():
926                     lines.append("   * :class:`%s.%s`\n" % (base.identifier, identifier))
927
928                 for identifier, py_prop in base.get_py_properties():
929                     lines.append("   * :class:`%s.%s`\n" % (base.identifier, identifier))
930
931             if lines:
932                 fw(".. rubric:: Inherited Properties\n\n")
933
934                 fw(".. hlist::\n")
935                 fw("   :columns: 2\n\n")
936
937                 for line in lines:
938                     fw(line)
939                 fw("\n")
940
941             # funcs
942             lines[:] = []
943
944             if _BPY_STRUCT_FAKE:
945                 for key, descr in descr_items:
946                     if type(descr) == MethodDescriptorType:
947                         lines.append("   * :class:`%s.%s`\n" % (_BPY_STRUCT_FAKE, key))
948
949             for base in bases:
950                 for func in base.functions:
951                     lines.append("   * :class:`%s.%s`\n" % (base.identifier, func.identifier))
952                 for identifier, py_func in base.get_py_functions():
953                     lines.append("   * :class:`%s.%s`\n" % (base.identifier, identifier))
954
955             if lines:
956                 fw(".. rubric:: Inherited Functions\n\n")
957
958                 fw(".. hlist::\n")
959                 fw("   :columns: 2\n\n")
960
961                 for line in lines:
962                     fw(line)
963                 fw("\n")
964
965             lines[:] = []
966
967         if struct.references:
968             # use this otherwise it gets in the index for a normal heading.
969             fw(".. rubric:: References\n\n")
970
971             fw(".. hlist::\n")
972             fw("   :columns: 2\n\n")
973
974             for ref in struct.references:
975                 ref_split = ref.split(".")
976                 if len(ref_split) > 2:
977                     ref = ref_split[-2] + "." + ref_split[-1]
978                 fw("   * :class:`%s`\n" % ref)
979             fw("\n")
980
981         # docs last?, disable for now
982         # write_example_ref("", fw, "bpy.types.%s" % struct_id)
983         file.close()
984
985     if "bpy.types" not in EXCLUDE_MODULES:
986         for struct in structs.values():
987             # TODO, rna_info should filter these out!
988             if "_OT_" in struct.identifier:
989                 continue
990             write_struct(struct)
991
992         def fake_bpy_type(class_value, class_name, descr_str, use_subclasses=True):
993             filepath = os.path.join(BASEPATH, "bpy.types.%s.rst" % class_name)
994             file = open(filepath, "w", encoding="utf-8")
995             fw = file.write
996
997             write_title(fw, class_name, "=")
998
999             fw(".. module:: bpy.types\n")
1000             fw("\n")
1001
1002             if use_subclasses:
1003                 subclass_ids = [s.identifier for s in structs.values() if s.base is None if not rna_info.rna_id_ignore(s.identifier)]
1004                 if subclass_ids:
1005                     fw("subclasses --- \n" + ", ".join((":class:`%s`" % s) for s in sorted(subclass_ids)) + "\n\n")
1006
1007             fw(".. class:: %s\n\n" % class_name)
1008             fw("   %s\n\n" % descr_str)
1009             fw("   .. note::\n\n")
1010             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)
1011
1012             descr_items = [(key, descr) for key, descr in sorted(class_value.__dict__.items()) if not key.startswith("__")]
1013
1014             for key, descr in descr_items:
1015                 if type(descr) == MethodDescriptorType:  # GetSetDescriptorType, GetSetDescriptorType's are not documented yet
1016                     py_descr2sphinx("   ", fw, descr, "bpy.types", class_name, key)
1017
1018             for key, descr in descr_items:
1019                 if type(descr) == GetSetDescriptorType:
1020                     py_descr2sphinx("   ", fw, descr, "bpy.types", class_name, key)
1021             file.close()
1022
1023         # write fake classes
1024         if _BPY_STRUCT_FAKE:
1025             class_value = bpy.types.Struct.__bases__[0]
1026             fake_bpy_type(class_value, _BPY_STRUCT_FAKE, "built-in base class for all classes in bpy.types.", use_subclasses=True)
1027
1028         if _BPY_PROP_COLLECTION_FAKE:
1029             class_value = bpy.data.objects.__class__
1030             fake_bpy_type(class_value, _BPY_PROP_COLLECTION_FAKE, "built-in class used for all collections.", use_subclasses=False)
1031
1032     # operators
1033     def write_ops():
1034         API_BASEURL = "http://svn.blender.org/svnroot/bf-blender/trunk/blender/release/scripts"
1035         API_BASEURL_ADDON = "http://svn.blender.org/svnroot/bf-extensions/trunk/py/scripts"
1036         API_BASEURL_ADDON_CONTRIB = "http://svn.blender.org/svnroot/bf-extensions/contrib/py/scripts"
1037
1038         op_modules = {}
1039         for op in ops.values():
1040             op_modules.setdefault(op.module_name, []).append(op)
1041         del op
1042
1043         for op_module_name, ops_mod in op_modules.items():
1044             filepath = os.path.join(BASEPATH, "bpy.ops.%s.rst" % op_module_name)
1045             file = open(filepath, "w", encoding="utf-8")
1046             fw = file.write
1047
1048             title = "%s Operators" % op_module_name.replace("_", " ").title()
1049
1050             write_title(fw, title, "=")
1051
1052             fw(".. module:: bpy.ops.%s\n\n" % op_module_name)
1053
1054             ops_mod.sort(key=lambda op: op.func_name)
1055
1056             for op in ops_mod:
1057                 args_str = ", ".join(prop.get_arg_default(force=True) for prop in op.args)
1058                 fw(".. function:: %s(%s)\n\n" % (op.func_name, args_str))
1059
1060                 # if the description isn't valid, we output the standard warning
1061                 # with a link to the wiki so that people can help
1062                 if not op.description or op.description == "(undocumented operator)":
1063                     operator_description = undocumented_message('bpy.ops', op.module_name, op.func_name)
1064                 else:
1065                     operator_description = op.description
1066
1067                 fw("   %s\n\n" % operator_description)
1068                 for prop in op.args:
1069                     write_param("   ", fw, prop)
1070                 if op.args:
1071                     fw("\n")
1072
1073                 location = op.get_location()
1074                 if location != (None, None):
1075                     if location[0].startswith("addons_contrib" + os.sep):
1076                         url_base = API_BASEURL_ADDON_CONTRIB
1077                     elif location[0].startswith("addons" + os.sep):
1078                         url_base = API_BASEURL_ADDON
1079                     else:
1080                         url_base = API_BASEURL
1081
1082                     fw("   :file: `%s <%s/%s>`_:%d\n\n" % (location[0], url_base, location[0], location[1]))
1083
1084             file.close()
1085
1086     if "bpy.ops" not in EXCLUDE_MODULES:
1087         write_ops()
1088
1089
1090 def rna2sphinx(BASEPATH):
1091
1092     try:
1093         os.mkdir(BASEPATH)
1094     except:
1095         pass
1096
1097     # conf.py - empty for now
1098     filepath = os.path.join(BASEPATH, "conf.py")
1099     file = open(filepath, "w", encoding="utf-8")
1100     fw = file.write
1101
1102     version_string = ".".join(str(v) for v in bpy.app.version)
1103     if bpy.app.build_revision != "Unknown":
1104         version_string = version_string + " r" + bpy.app.build_revision
1105
1106     version_string_fp = "_".join(str(v) for v in bpy.app.version)
1107
1108     if bpy.app.version_cycle == "release":
1109         version_string_pdf = "%s%s_release" % ("_".join(str(v) for v in bpy.app.version[:2]), bpy.app.version_char)
1110     else:
1111         version_string_pdf = version_string_fp
1112
1113     fw("project = 'Blender'\n")
1114     # fw("master_doc = 'index'\n")
1115     fw("copyright = u'Blender Foundation'\n")
1116     fw("version = '%s - API'\n" % version_string)
1117     fw("release = '%s - API'\n" % version_string)
1118
1119     # until we get a theme for 'Naiad'
1120     if 0:
1121         fw("html_theme = 'blender-org'\n")
1122         fw("html_theme_path = ['../']\n")
1123
1124         # copied with the theme, exclude else we get an error [#28873]
1125         fw("html_favicon = 'favicon.ico'\n")
1126
1127     # not helpful since the source us generated, adds to upload size.
1128     fw("html_copy_source = False\n")
1129     fw("\n")
1130     # needed for latex, pdf gen
1131     fw("latex_documents = [ ('contents', 'contents.tex', 'Blender Index', 'Blender Foundation', 'manual'), ]\n")
1132     fw("latex_paper_size = 'a4paper'\n")
1133     file.close()
1134
1135     # main page needed for sphinx (index.html)
1136     filepath = os.path.join(BASEPATH, "contents.rst")
1137     file = open(filepath, "w", encoding="utf-8")
1138     fw = file.write
1139
1140     fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
1141     fw(" Blender Documentation contents\n")
1142     fw("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n")
1143     fw("\n")
1144     fw("Welcome, this document is an API reference for Blender %s. built %s.\n" % (version_string, bpy.app.build_date))
1145     fw("\n")
1146
1147     # fw("`A PDF version of this document is also available <blender_python_reference_%s.pdf>`_\n" % version_string_pdf)
1148     fw("`A compressed ZIP file of this site is available <blender_python_reference_%s.zip>`_\n" % version_string_pdf)
1149
1150     fw("\n")
1151
1152     if not EXCLUDE_INFO_DOCS:
1153         fw("============================\n")
1154         fw("Blender/Python Documentation\n")
1155         fw("============================\n")
1156         fw("\n")
1157         fw("\n")
1158         fw(".. toctree::\n")
1159         fw("   :maxdepth: 1\n\n")
1160         for info, info_desc in INFO_DOCS:
1161             fw("   %s <%s>\n\n" % (info_desc, info))
1162         fw("\n")
1163
1164     fw("===================\n")
1165     fw("Application Modules\n")
1166     fw("===================\n")
1167     fw("\n")
1168     fw(".. toctree::\n")
1169     fw("   :maxdepth: 1\n\n")
1170     if "bpy.context" not in EXCLUDE_MODULES:
1171         fw("   bpy.context.rst\n\n")  # note: not actually a module
1172     if "bpy.data" not in EXCLUDE_MODULES:
1173         fw("   bpy.data.rst\n\n")  # note: not actually a module
1174     if "bpy.ops" not in EXCLUDE_MODULES:
1175         fw("   bpy.ops.rst\n\n")
1176     if "bpy.types" not in EXCLUDE_MODULES:
1177         fw("   bpy.types.rst\n\n")
1178
1179     # py modules
1180     if "bpy.utils" not in EXCLUDE_MODULES:
1181         fw("   bpy.utils.rst\n\n")
1182     if "bpy.path" not in EXCLUDE_MODULES:
1183         fw("   bpy.path.rst\n\n")
1184     if "bpy.app" not in EXCLUDE_MODULES:
1185         fw("   bpy.app.rst\n\n")
1186     if "bpy.app.handlers" not in EXCLUDE_MODULES:
1187         fw("   bpy.app.handlers.rst\n\n")
1188
1189     # C modules
1190     if "bpy.props" not in EXCLUDE_MODULES:
1191         fw("   bpy.props.rst\n\n")
1192
1193     fw("==================\n")
1194     fw("Standalone Modules\n")
1195     fw("==================\n")
1196     fw("\n")
1197     fw(".. toctree::\n")
1198     fw("   :maxdepth: 1\n\n")
1199
1200     if "mathutils" not in EXCLUDE_MODULES:
1201         fw("   mathutils.rst\n\n")
1202     if "mathutils.geometry" not in EXCLUDE_MODULES:
1203         fw("   mathutils.geometry.rst\n\n")
1204     if "mathutils.noise" not in EXCLUDE_MODULES:
1205         fw("   mathutils.noise.rst\n\n")
1206     if "Freestyle" not in EXCLUDE_MODULES:
1207         fw("   Freestyle.rst\n\n")
1208     if "bgl" not in EXCLUDE_MODULES:
1209         fw("   bgl.rst\n\n")
1210     if "blf" not in EXCLUDE_MODULES:
1211         fw("   blf.rst\n\n")
1212     if "gpu" not in EXCLUDE_MODULES:
1213         fw("   gpu.rst\n\n")
1214     if "aud" not in EXCLUDE_MODULES:
1215         fw("   aud.rst\n\n")
1216     if "bpy_extras" not in EXCLUDE_MODULES:
1217         fw("   bpy_extras.rst\n\n")
1218
1219     # game engine
1220     if "bge" not in EXCLUDE_MODULES:
1221         fw("===================\n")
1222         fw("Game Engine Modules\n")
1223         fw("===================\n")
1224         fw("\n")
1225         fw(".. toctree::\n")
1226         fw("   :maxdepth: 1\n\n")
1227         fw("   bge.types.rst\n\n")
1228         fw("   bge.logic.rst\n\n")
1229         fw("   bge.render.rst\n\n")
1230         fw("   bge.texture.rst\n\n")
1231         fw("   bge.events.rst\n\n")
1232         fw("   bge.constraints.rst\n\n")
1233
1234     # rna generated change log
1235     fw("========\n")
1236     fw("API Info\n")
1237     fw("========\n")
1238     fw("\n")
1239     fw(".. toctree::\n")
1240     fw("   :maxdepth: 1\n\n")
1241     fw("   change_log.rst\n\n")
1242
1243     fw("\n")
1244     fw("\n")
1245     fw(".. note:: The Blender Python API has areas which are still in development.\n")
1246     fw("   \n")
1247     fw("   The following areas are subject to change.\n")
1248     fw("      * operator behavior, names and arguments\n")
1249     fw("      * mesh creation and editing functions\n")
1250     fw("   \n")
1251     fw("   These parts of the API are relatively stable and are unlikely to change significantly\n")
1252     fw("      * data API, access to attributes of blender data such as mesh verts, material color, timeline frames and scene objects\n")
1253     fw("      * user interface functions for defining buttons, creation of menus, headers, panels\n")
1254     fw("      * render engine integration\n")
1255     fw("      * modules: bgl, mathutils & game engine.\n")
1256     fw("\n")
1257
1258     file.close()
1259
1260     # internal modules
1261     if "bpy.ops" not in EXCLUDE_MODULES:
1262         filepath = os.path.join(BASEPATH, "bpy.ops.rst")
1263         file = open(filepath, "w", encoding="utf-8")
1264         fw = file.write
1265         fw("Operators (bpy.ops)\n")
1266         fw("===================\n\n")
1267         write_example_ref("", fw, "bpy.ops")
1268         fw(".. toctree::\n")
1269         fw("   :glob:\n\n")
1270         fw("   bpy.ops.*\n\n")
1271         file.close()
1272
1273     if "bpy.types" not in EXCLUDE_MODULES:
1274         filepath = os.path.join(BASEPATH, "bpy.types.rst")
1275         file = open(filepath, "w", encoding="utf-8")
1276         fw = file.write
1277         fw("Types (bpy.types)\n")
1278         fw("=================\n\n")
1279         fw(".. toctree::\n")
1280         fw("   :glob:\n\n")
1281         fw("   bpy.types.*\n\n")
1282         file.close()
1283
1284     if "bpy.data" not in EXCLUDE_MODULES:
1285         # not actually a module, only write this file so we
1286         # can reference in the TOC
1287         filepath = os.path.join(BASEPATH, "bpy.data.rst")
1288         file = open(filepath, "w", encoding="utf-8")
1289         fw = file.write
1290         fw("Data Access (bpy.data)\n")
1291         fw("======================\n\n")
1292         fw(".. module:: bpy\n")
1293         fw("\n")
1294         fw("This module is used for all blender/python access.\n")
1295         fw("\n")
1296         fw(".. data:: data\n")
1297         fw("\n")
1298         fw("   Access to blenders internal data\n")
1299         fw("\n")
1300         fw("   :type: :class:`bpy.types.BlendData`\n")
1301         fw("\n")
1302         fw(".. literalinclude:: ../examples/bpy.data.py\n")
1303         file.close()
1304
1305     EXAMPLE_SET_USED.add("bpy.data")
1306
1307     module = None
1308
1309     if "bpy.context" not in EXCLUDE_MODULES:
1310         # one of a kind, context doc (uses ctypes to extract info!)
1311         pycontext2sphinx(BASEPATH)
1312
1313     # python modules
1314     if "bpy.utils" not in EXCLUDE_MODULES:
1315         from bpy import utils as module
1316         pymodule2sphinx(BASEPATH, "bpy.utils", module, "Utilities")
1317
1318     if "bpy.path" not in EXCLUDE_MODULES:
1319         from bpy import path as module
1320         pymodule2sphinx(BASEPATH, "bpy.path", module, "Path Utilities")
1321
1322     if "bpy_extras" not in EXCLUDE_MODULES:
1323         import bpy_extras as module
1324         pymodule2sphinx(BASEPATH, "bpy_extras", module, "Extra Utilities")
1325
1326     # C modules
1327     if "bpy.app" not in EXCLUDE_MODULES:
1328         from bpy import app as module
1329         pymodule2sphinx(BASEPATH, "bpy.app", module, "Application Data")
1330
1331     if "bpy.app.handlers" not in EXCLUDE_MODULES:
1332         from bpy.app import handlers as module
1333         pymodule2sphinx(BASEPATH, "bpy.app.handlers", module, "Application Handlers")
1334
1335     if "bpy.props" not in EXCLUDE_MODULES:
1336         from bpy import props as module
1337         pymodule2sphinx(BASEPATH, "bpy.props", module, "Property Definitions")
1338
1339     if "mathutils" not in EXCLUDE_MODULES:
1340         import mathutils as module
1341         pymodule2sphinx(BASEPATH, "mathutils", module, "Math Types & Utilities")
1342
1343     if "mathutils.geometry" not in EXCLUDE_MODULES:
1344         import mathutils.geometry as module
1345         pymodule2sphinx(BASEPATH, "mathutils.geometry", module, "Geometry Utilities")
1346
1347     if "mathutils.noise" not in EXCLUDE_MODULES:
1348         import mathutils.noise as module
1349         pymodule2sphinx(BASEPATH, "mathutils.noise", module, "Noise Utilities")
1350
1351     if "Freestyle" not in EXCLUDE_MODULES:
1352         import Freestyle as module
1353         pymodule2sphinx(BASEPATH, "Freestyle", module, "Freestyle Data Types & Operators")
1354
1355     if "blf" not in EXCLUDE_MODULES:
1356         import blf as module
1357         pymodule2sphinx(BASEPATH, "blf", module, "Font Drawing")
1358
1359     if "bgl" not in EXCLUDE_MODULES:
1360         #import bgl as module
1361         #pymodule2sphinx(BASEPATH, "bgl", module, "Blender OpenGl wrapper")
1362         #del module
1363         import shutil
1364         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bgl.rst"), BASEPATH)
1365
1366     if "gpu" not in EXCLUDE_MODULES:
1367         #import gpu as module
1368         #pymodule2sphinx(BASEPATH, "gpu", module, "GPU Shader Module")
1369         #del module
1370         import shutil
1371         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "gpu.rst"), BASEPATH)
1372
1373     if "aud" not in EXCLUDE_MODULES:
1374         import aud as module
1375         pymodule2sphinx(BASEPATH, "aud", module, "Audio System")
1376     del module
1377
1378     ## game engine
1379     import shutil
1380     # copy2 keeps time/date stamps
1381     if "bge" not in EXCLUDE_MODULES:
1382         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.types.rst"), BASEPATH)
1383         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.logic.rst"), BASEPATH)
1384         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.render.rst"), BASEPATH)
1385         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.texture.rst"), BASEPATH)
1386         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.events.rst"), BASEPATH)
1387         shutil.copy2(os.path.join(BASEPATH, "..", "rst", "bge.constraints.rst"), BASEPATH)
1388
1389     shutil.copy2(os.path.join(BASEPATH, "..", "rst", "change_log.rst"), BASEPATH)
1390
1391     if not EXCLUDE_INFO_DOCS:
1392         for info, info_desc in INFO_DOCS:
1393             shutil.copy2(os.path.join(BASEPATH, "..", "rst", info), BASEPATH)
1394
1395     if 0:
1396         filepath = os.path.join(BASEPATH, "bpy.rst")
1397         file = open(filepath, "w", encoding="utf-8")
1398         fw = file.write
1399
1400         fw("\n")
1401
1402         title = ":mod:`bpy` --- Blender Python Module"
1403
1404         write_title(fw, title, "=")
1405
1406         fw(".. module:: bpy.types\n\n")
1407         file.close()
1408
1409     # bpy.types and bpy.ops
1410     pyrna2sphinx(BASEPATH)
1411
1412     file.close()
1413
1414
1415 def main():
1416     import shutil
1417
1418     script_dir = os.path.dirname(__file__)
1419     path_in = os.path.join(script_dir, "sphinx-in")
1420     path_out = os.path.join(script_dir, "sphinx-out")
1421     path_examples = os.path.join(script_dir, "examples")
1422     # only for partial updates
1423     path_in_tmp = path_in + "-tmp"
1424
1425     if not os.path.exists(path_in):
1426         os.mkdir(path_in)
1427
1428     for f in os.listdir(path_examples):
1429         if f.endswith(".py"):
1430             EXAMPLE_SET.add(os.path.splitext(f)[0])
1431
1432     # only for full updates
1433     if _BPY_FULL_REBUILD:
1434         shutil.rmtree(path_in, True)
1435         shutil.rmtree(path_out, True)
1436     else:
1437         # write here, then move
1438         shutil.rmtree(path_in_tmp, True)
1439
1440     rna2sphinx(path_in_tmp)
1441
1442     if not _BPY_FULL_REBUILD:
1443         import filecmp
1444
1445         # now move changed files from 'path_in_tmp' --> 'path_in'
1446         file_list_path_in = set(os.listdir(path_in))
1447         file_list_path_in_tmp = set(os.listdir(path_in_tmp))
1448
1449         # remove deprecated files that have been removed.
1450         for f in sorted(file_list_path_in):
1451             if f not in file_list_path_in_tmp:
1452                 print("\tdeprecated: %s" % f)
1453                 os.remove(os.path.join(path_in, f))
1454
1455         # freshen with new files.
1456         for f in sorted(file_list_path_in_tmp):
1457             f_from = os.path.join(path_in_tmp, f)
1458             f_to = os.path.join(path_in, f)
1459
1460             do_copy = True
1461             if f in file_list_path_in:
1462                 if filecmp.cmp(f_from, f_to):
1463                     do_copy = False
1464
1465             if do_copy:
1466                 print("\tupdating: %s" % f)
1467                 shutil.copy(f_from, f_to)
1468             '''else:
1469                 print("\tkeeping: %s" % f) # eh, not that useful'''
1470
1471     EXAMPLE_SET_UNUSED = EXAMPLE_SET - EXAMPLE_SET_USED
1472     if EXAMPLE_SET_UNUSED:
1473         print("\nUnused examples found in '%s'..." % path_examples)
1474         for f in EXAMPLE_SET_UNUSED:
1475             print("    %s.py" % f)
1476         print("  %d total\n" % len(EXAMPLE_SET_UNUSED))
1477
1478     import sys
1479     sys.exit()
1480
1481 if __name__ == '__main__':
1482     main()