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