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