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