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