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