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