1 # ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 # Contributor(s): Campbell Barton
19 # #**** END GPL LICENSE BLOCK #****
23 run this script from blenders root path once you have compiled blender
24 ./blender.bin -P source/blender/python/epy_doc_gen.py
26 This will generate python files in "./source/blender/python/doc/bpy"
27 Generate html docs by running...
29 epydoc source/blender/python/doc/bpy/ -v \\
30 -o source/blender/python/doc/html \\
31 --inheritance=included \\
38 # if you dont have graphvis installed ommit the --graph arg.
40 # GLOBALS['BASEDIR'] = './source/blender/python/doc'
45 INIT_SUBMODULES = {} # store initialized files
47 INIT_SUBMODULES_IMPORTS = {} # dont import the same module twice
49 def append_package(package_path, mod_name):
51 init_path = os.path.join(os.path.dirname(package_path), "__init__.py")
55 imports = INIT_SUBMODULES_IMPORTS.setdefault(init_path, [])
56 if mod_name in imports:
58 imports.append(mod_name)
61 os.makedirs(os.path.dirname(init_path)) # make the dirs if they are not there
65 # Open the new file for the first time, otherwise keep it open.
66 f = INIT_SUBMODULES.get(init_path)
68 f = INIT_SUBMODULES[init_path] = open(init_path, 'w')
71 f.write("import %s\n" % mod_name)
75 def append_package_recursive(package_path, BASEPATH):
77 assume the last item of package_path will be a file (not a dir thats created)
80 package_path = os.path.splitext(package_path)[0] # incase of .py
83 os.makedirs(os.path.join(BASEPATH, os.path.dirname(package_path))) # make the dirs if they are not there
89 for mod_name in package_path.split(os.sep):
90 init_path = os.path.join(new_path, "__init__.py")
91 new_path = os.path.join(new_path, mod_name)
92 append_package(init_path, mod_name)
95 def open_submodule(subpath, BASEPATH):
97 This is a utility function that lets us quickly add submodules
100 # create all the package paths leading up to this module
101 append_package_recursive(subpath, BASEPATH)
103 module_name = os.path.basename( os.path.splitext(subpath)[0] )
104 mod_path = os.path.join(BASEPATH, subpath)
106 # Open the new file for the first time, otherwise keep it open.
107 f = SUBMODULES.get(mod_path)
109 f = SUBMODULES[mod_path] = open(mod_path, 'w')
111 f = open(mod_path, 'w')
115 for files in (INIT_SUBMODULES.values(), SUBMODULES.values()):
117 if f.name.endswith('.py'):
121 f = open(f_name, 'a')
122 f.write("\ndel __package__\n") # annoying, no need do show this
129 if val < -10000000: return '-inf'
130 if val > 10000000: return 'inf'
136 def get_array_str(length):
137 if length > 0: return ' array of %d items' % length
140 def full_rna_struct_path(rna_struct):
142 Needed when referencing one struct from another
144 nested = rna_struct.nested
146 return "%s.%s" % (full_rna_struct_path(nested), rna_struct.identifier)
148 return rna_struct.identifier
150 def rna_id_ignore(rna_id):
151 if rna_id == "rna_type":
163 def write_func(rna, ident, out, func_type):
165 kw_args = [] # "foo = 1", "bar=0.5", "spam='ENUM'"
166 kw_arg_attrs = [] # "@type mode: int"
168 rna_struct= rna.rna_type
170 # Operators and functions work differently
171 if func_type=='OPERATOR':
172 rna_func_name = rna_struct.identifier.split("_OT_")[-1]
173 rna_func_desc = rna_struct.description.strip()
174 items = rna_struct.properties.items()
176 rna_func_name = rna.identifier
177 rna_func_desc = rna.description.strip()
178 items = rna.parameters.items()
181 for rna_prop_identifier, rna_prop in items:
182 if rna_id_ignore(rna_prop_identifier):
186 val = val_error = val_str = rna_prop_type = None
188 # ['rna_type', 'name', 'array_length', 'description', 'hard_max', 'hard_min', 'identifier', 'precision', 'readonly', 'soft_max', 'soft_min', 'step', 'subtype', 'type']
189 #rna_prop= op_rna.rna_type.properties[attr]
190 rna_prop_type = rna_prop.type.lower() # enum, float, int, boolean
193 # only for rna functions, operators should not get pointers as args
194 if rna_prop_type=='pointer':
195 rna_prop_type_refine = "L{%s}" % rna_prop.fixed_type.identifier
197 # Collections/Arrays can have a srna type
198 rna_prop_srna_type = rna_prop.srna
199 if rna_prop_srna_type:
200 print(rna_prop_srna_type.identifier)
201 rna_prop_type_refine = "L{%s}" % rna_prop_srna_type.identifier
203 rna_prop_type_refine = rna_prop_type
205 del rna_prop_srna_type
208 try: length = rna_prop.array_length
211 array_str = get_array_str(length)
213 if rna_prop.use_return:
214 kw_type_str= "@rtype: %s%s" % (rna_prop_type_refine, array_str)
215 kw_param_str= "@return: %s" % (rna_prop.description.strip())
217 kw_type_str= "@type %s: %s%s" % (rna_prop_identifier, rna_prop_type_refine, array_str)
218 kw_param_str= "@param %s: %s" % (rna_prop_identifier, rna_prop.description.strip())
222 if func_type=='OPERATOR':
224 val = getattr(rna, rna_prop_identifier)
227 val = "'<UNDEFINED>'"
233 elif rna_prop_type=='float':
236 if '.' not in val_str and '-' not in val_str: # value could be 1e-05
240 val_str = str(tuple(val))
242 kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max)))
245 elif rna_prop_type=='int':
249 val_str = str(tuple(val))
251 # print(dir(rna_prop))
252 kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max)))
253 # These strings dont have a max length???
254 #kw_param_str += ' (maximum length of %s)' % (rna_prop.max_length)
257 elif rna_prop_type=='boolean':
259 if val: val_str='True'
260 else: val_str='False'
262 val_str = str(tuple(val))
264 elif rna_prop_type=='enum':
268 kw_param_str += (' in (%s)' % ', '.join(rna_prop.items.keys()))
272 elif rna_prop_type=='string':
276 # todo - collection - array
277 # print (rna_prop.type)
279 kw_args.append('%s = %s' % (rna_prop_identifier, val_str))
283 # currently functions dont have a default value
284 if not rna_prop.use_return:
285 kw_args.append('%s' % (rna_prop_identifier))
290 # Same for operators and functions
291 kw_arg_attrs.append(kw_type_str)
293 kw_arg_attrs.append(kw_param_str)
297 out.write(ident+'def %s(%s):\n' % (rna_func_name, ', '.join(kw_args)))
298 out.write(ident+'\t"""\n')
300 # Descriptions could be multiline
301 for rna_func_desc_line in rna_func_desc.split('\n'):
302 out.write(ident+'\t%s\n' % rna_func_desc_line.strip())
304 for desc in kw_arg_attrs:
305 out.write(ident+'\t%s\n' % desc)
307 # out.write(ident+'\t@rtype: None\n') # implicit
308 out.write(ident+'\t"""\n')
312 def rna2epy(BASEPATH):
314 # Use for faster lookups
315 # use rna_struct.identifier as the key for each dict
316 rna_struct_dict = {} # store identifier:rna lookups
317 rna_full_path_dict = {} # store the result of full_rna_struct_path(rna_struct)
318 rna_children_dict = {} # store all rna_structs nested from here
319 rna_references_dict = {} # store a list of rna path strings that reference this type
320 rna_functions_dict = {} # store all functions directly in this type (not inherited)
323 # def write_func(rna_func, ident):
326 def write_struct(rna_struct, ident):
327 identifier = rna_struct.identifier
329 rna_base = rna_struct.base
332 out.write(ident+ 'class %s(%s):\n' % (identifier, rna_base.identifier))
333 rna_base_prop_keys = rna_base.properties.keys() # could be cached
334 rna_base_func_keys = [f.identifier for f in rna_base.functions]
336 out.write(ident+ 'class %s:\n' % identifier)
337 rna_base_prop_keys = []
338 rna_base_func_keys = []
340 out.write(ident+ '\t"""\n')
342 title = 'The %s Object' % rna_struct.name
343 description = rna_struct.description.strip()
344 out.write(ident+ '\t%s\n' % title)
345 out.write(ident+ '\t%s\n' % ('=' * len(title)))
346 out.write(ident+ '\t\t%s\n' % description)
347 rna_words.update(description.split())
350 # For convenience, give a list of all places were used.
351 rna_refs= rna_references_dict[identifier]
354 out.write(ident+ '\t\t\n')
355 out.write(ident+ '\t\tReferences\n')
356 out.write(ident+ '\t\t==========\n')
358 for rna_ref_string in rna_refs:
359 out.write(ident+ '\t\t\t- L{%s}\n' % rna_ref_string)
361 out.write(ident+ '\t\t\n')
364 out.write(ident+ '\t\t\n')
365 out.write(ident+ '\t\t(no references to this struct found)\n')
366 out.write(ident+ '\t\t\n')
368 for rna_prop_identifier, rna_prop in rna_struct.properties.items():
370 if rna_prop_identifier=='RNA': continue
371 if rna_id_ignore(rna_prop_identifier): continue
372 if rna_prop_identifier in rna_base_prop_keys: continue # does this prop exist in our parent class, if so skip
374 rna_desc = rna_prop.description.strip()
376 if rna_desc: rna_words.update(rna_desc.split())
377 if not rna_desc: rna_desc = rna_prop.name
378 if not rna_desc: rna_desc = 'Note - No documentation for this property!'
380 rna_prop_type = rna_prop.type.lower()
382 if rna_prop_type=='collection': collection_str = 'Collection of '
383 else: collection_str = ''
385 # some collections have a srna for their own properties
386 # TODO - arrays, however this isnt used yet
387 rna_prop_srna_type = rna_prop.srna
388 if rna_prop_srna_type:
389 collection_str = "L{%s} %s" % (rna_prop_srna_type.identifier, collection_str)
390 del rna_prop_srna_type
392 try: rna_prop_ptr = rna_prop.fixed_type
393 except: rna_prop_ptr = None
395 try: length = rna_prop.array_length
398 array_str = get_array_str(length)
400 if rna_prop.editable: readonly_str = ''
401 else: readonly_str = ' (readonly)'
403 if rna_prop_ptr: # Use the pointer type
404 out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc))
405 out.write(ident+ '\t@type %s: %sL{%s}%s%s\n' % (rna_prop_identifier, collection_str, rna_prop_ptr.identifier, array_str, readonly_str))
407 if rna_prop_type == 'enum':
409 out.write(ident+ '\t@ivar %s: %s in (%s)\n' % (rna_prop_identifier, rna_desc, ', '.join(rna_prop.items.keys())))
411 out.write(ident+ '\t@ivar %s: %s in...\n' % (rna_prop_identifier, rna_desc))
412 for e, e_rna_prop in rna_prop.items.items():
413 #out.write(ident+ '\t\t- %s: %s\n' % (e, e_rna_prop.description)) # XXX - segfaults, FIXME
414 out.write(ident+ '\t\t- %s\n' % e)
416 out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
417 elif rna_prop_type == 'int' or rna_prop_type == 'float':
418 out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc))
419 out.write(ident+ '\t@type %s: %s%s%s in [%s, %s]\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str, range_str(rna_prop.hard_min), range_str(rna_prop.hard_max) ))
420 elif rna_prop_type == 'string':
421 out.write(ident+ '\t@ivar %s: %s (maximum length of %s)\n' % (rna_prop_identifier, rna_desc, rna_prop.max_length))
422 out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
424 out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc))
425 out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str))
428 out.write(ident+ '\t"""\n\n')
432 # for rna_func in rna_struct.functions: # Better ignore inherited (line below)
433 for rna_func in rna_functions_dict[identifier]:
434 if rna_func not in rna_base_func_keys:
435 write_func(rna_func, ident+'\t', out, 'FUNCTION')
439 # Now write children recursively
440 for child in rna_children_dict[identifier]:
441 write_struct(child, ident + '\t')
445 # out = open(target_path, 'w')
446 out = open_submodule("types.py", BASEPATH) # bpy.types
448 def base_id(rna_struct):
449 try: return rna_struct.base.identifier
450 except: return '' # invalid id
452 #structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()]
455 for rna_struct in bpy.doc.structs.values():
456 structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) )
459 for rna_type_name in dir(bpy.types):
460 rna_type = getattr(bpy.types, rna_type_name)
462 try: rna_struct = rna_type.bl_rna
463 except: rna_struct = None
466 #if not rna_type_name.startswith('__'):
468 identifier = rna_struct.identifier
470 if not rna_id_ignore(identifier):
471 structs.append( (base_id(rna_struct), identifier, rna_struct) )
474 rna_struct_dict[identifier] = rna_struct
476 # Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings'
477 rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct)
479 # Store a list of functions, remove inherited later
480 rna_functions_dict[identifier]= list(rna_struct.functions)
483 # fill in these later
484 rna_children_dict[identifier]= []
485 rna_references_dict[identifier]= []
489 print("Ignoring", rna_type_name)
492 # Sucks but we need to copy this so we can check original parent functions
493 rna_functions_dict__copy = {}
494 for key, val in rna_functions_dict.items():
495 rna_functions_dict__copy[key] = val[:]
498 structs.sort() # not needed but speeds up sort below, setting items without an inheritance first
500 # Arrange so classes are always defined in the correct order
502 while deps_ok == False:
506 for i, (rna_base, identifier, rna_struct) in enumerate(structs):
508 rna_done.add(identifier)
510 if rna_base and rna_base not in rna_done:
512 data = structs.pop(i)
514 while i < len(structs):
515 if structs[i][1]==rna_base:
516 structs.insert(i+1, data) # insert after the item we depend on.
522 print('Dependancy "%s" could not be found for "%s"' % (identifier, rna_base))
526 # Done ordering structs
529 # precalc vars to avoid a lot of looping
530 for (rna_base, identifier, rna_struct) in structs:
533 rna_base_prop_keys = rna_struct_dict[rna_base].properties.keys() # could cache
534 rna_base_func_keys = [f.identifier for f in rna_struct_dict[rna_base].functions]
536 rna_base_prop_keys = []
537 rna_base_func_keys= []
539 # rna_struct_path = full_rna_struct_path(rna_struct)
540 rna_struct_path = rna_full_path_dict[identifier]
542 for rna_prop_identifier, rna_prop in rna_struct.properties.items():
544 if rna_prop_identifier=='RNA': continue
545 if rna_id_ignore(rna_prop_identifier): continue
546 if rna_prop_identifier in rna_base_prop_keys: continue
549 for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", None)):
550 # Does this property point to me?
552 rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_prop_identifier) )
554 for rna_func in rna_struct.functions:
555 for rna_prop_identifier, rna_prop in rna_func.parameters.items():
557 if rna_prop_identifier=='RNA': continue
558 if rna_id_ignore(rna_prop_identifier): continue
559 if rna_prop_identifier in rna_base_func_keys: continue
562 try: rna_prop_ptr = rna_prop.fixed_type
563 except: rna_prop_ptr = None
565 # Does this property point to me?
567 rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_func.identifier) )
570 # Store nested children
571 nested = rna_struct.nested
573 rna_children_dict[nested.identifier].append(rna_struct)
577 rna_funcs = rna_functions_dict[identifier]
579 # Remove inherited functions if we have any
580 rna_base_funcs = rna_functions_dict__copy[rna_base]
581 rna_funcs[:] = [f for f in rna_funcs if f not in rna_base_funcs]
583 rna_functions_dict__copy.clear()
584 del rna_functions_dict__copy
586 # Sort the refs, just reads nicer
587 for rna_refs in rna_references_dict.values():
590 for (rna_base, identifier, rna_struct) in structs:
591 if rna_struct.nested:
594 write_struct(rna_struct, '')
600 # # We could also just run....
601 # os.system('epydoc source/blender/python/doc/rna.py -o ./source/blender/python/doc/html -v')
603 target_path = os.path.join(BASEPATH, "dump.py") # XXX - used for other funcs
606 out= open(target_path.replace('.py', '.dot'), 'w')
607 out.write('digraph "rna data api" {\n')
608 out.write('\tnode [style=filled, shape = "box"];\n')
609 out.write('\toverlap=false;\n')
610 out.write('\trankdir = LR;\n')
611 out.write('\tsplines=true;\n')
612 out.write('\tratio=auto;\n')
616 out.write('\tsize="10,10"\n')
617 #out.write('\tpage="8.5,11"\n')
618 #out.write('\tcenter=""\n')
620 def isop(rna_struct):
621 return '_OT_' in rna_struct.identifier
624 for (rna_base, identifier, rna_struct) in structs:
628 base = rna_struct.base
631 out.write('\t"%s";\n' % identifier)
633 for (rna_base, identifier, rna_struct) in structs:
638 base = rna_struct.base
640 if base and not isop(base):
641 out.write('\t"%s" -> "%s" [label="(base)" weight=1.0];\n' % (base.identifier, identifier))
643 nested = rna_struct.nested
644 if nested and not isop(nested):
645 out.write('\t"%s" -> "%s" [label="(nested)" weight=1.0];\n' % (nested.identifier, identifier))
649 rna_refs= rna_references_dict[identifier]
651 for rna_ref_string in rna_refs:
653 if '_OT_' in rna_ref_string:
656 ref = rna_ref_string.split('.')[-2]
657 out.write('\t"%s" -> "%s" [label="%s" weight=0.01];\n' % (ref, identifier, rna_ref_string))
663 # # We could also just run....
664 # os.system('dot source/blender/python/doc/rna.dot -Tsvg -o ./source/blender/python/doc/rna.svg')
667 out= open(target_path.replace('.py', '.words'), 'w')
668 rna_words = list(rna_words)
671 out.write('%s\n' % w)
674 def op2epy(BASEPATH):
675 # out = open(target_path, 'w')
677 op_mods = dir(bpy.ops)
678 op_mods.remove('add')
679 op_mods.remove('remove')
681 for op_mod_name in sorted(op_mods):
682 if op_mod_name.startswith('__'):
686 mod_path = os.path.join("ops", op_mod_name + ".py")
687 out = open_submodule(mod_path, BASEPATH)
690 op_mod = getattr(bpy.ops, op_mod_name)
691 operators = dir(op_mod)
692 for op in sorted(operators):
693 # rna = getattr(bpy.types, op).bl_rna
694 rna = getattr(op_mod, op).get_rna()
695 write_func(rna, '', out, 'OPERATOR')
700 def misc2epy(BASEPATH):
702 Hard coded modules, try to avoid adding stuff here
705 f = append_package(os.path.join(BASEPATH, ""), ""); # add a slash on the end of the base path
708 @type data: L{bpy.types.Main}
709 @var data: blender data is accessed from here
713 f = open_submodule("props.py", BASEPATH)
717 def BoolProperty(attr, name="", description="", default=False):
719 return a new bool property
721 def IntProperty(attr, name="", description="", min=-MAX_INT, max=MAX_INT, soft_min=-MAX_INT, soft_max=MAX_INT, default=0):
723 return a new int property
725 def FloatProperty(attr, name="", description="", min=-MAX_FLOAT, max=MAX_FLOAT, soft_min=-MAX_FLOAT, soft_max=MAX_FLOAT, default=0.0):
727 return a new float property
729 def StringProperty(attr, name="", description="", maxlen=0, default=""):
731 return a new string property
733 def EnumProperty(attr, items, name="", description="", default=""):
735 return a new enum property
740 if __name__ == '__main__':
741 if 'bpy' not in dir():
742 print("\nError, this script must run from inside blender2.5")
743 print(script_help_msg)
745 misc2epy('source/blender/python/doc/bpy') # first to write in info in some of the modules.
746 rna2epy('source/blender/python/doc/bpy')
747 op2epy('source/blender/python/doc/bpy')