diff options
Diffstat (limited to 'source/blender/python/epy_doc_gen.py')
-rw-r--r-- | source/blender/python/epy_doc_gen.py | 534 |
1 files changed, 534 insertions, 0 deletions
diff --git a/source/blender/python/epy_doc_gen.py b/source/blender/python/epy_doc_gen.py new file mode 100644 index 00000000000..9f3efc916bf --- /dev/null +++ b/source/blender/python/epy_doc_gen.py @@ -0,0 +1,534 @@ + # ***** BEGIN GPL LICENSE BLOCK ***** + # + # This program is free software; you can redistribute it and/or + # modify it under the terms of the GNU General Public License + # as published by the Free Software Foundation; either version 2 + # of the License, or (at your option) any later version. + # + # This program is distributed in the hope that it will be useful, + # but WITHOUT ANY WARRANTY; without even the implied warranty of + # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + # GNU General Public License for more details. + # + # You should have received a copy of the GNU General Public License + # along with this program; if not, write to the Free Software Foundation, + # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + # + # Contributor(s): Campbell Barton + # + # #**** END GPL LICENSE BLOCK #**** + +script_help_msg = ''' +Usage, +run this script from blenders root path once you have compiled blender + ./blender.bin -P source/blender/python/epy_doc_gen.py + +This will generate rna.py and bpyoperator.py in "./source/blender/python/doc/" +Generate html docs by running... + + epydoc source/blender/python/doc/*.py -v \\ + -o source/blender/python/doc/html \\ + --inheritance=included \\ + --no-sourcecode \\ + --graph=classtree \\ + --graph-font-size=8 + +''' + +# if you dont have graphvis installed ommit the --graph arg. + +def range_str(val): + if val < -10000000: return '-inf' + if val > 10000000: return 'inf' + if type(val)==float: + return '%g' % val + else: + return str(val) + +def get_array_str(length): + if length > 0: return ' array of %d items' % length + else: return '' + +def full_rna_struct_path(rna_struct): + ''' + Needed when referencing one struct from another + ''' + nested = rna_struct.nested + if nested: + return "%s.%s" % (full_rna_struct_path(nested), rna_struct.identifier) + else: + return rna_struct.identifier + +def write_func(rna, ident, out, func_type): + # Keyword attributes + kw_args = [] # "foo = 1", "bar=0.5", "spam='ENUM'" + kw_arg_attrs = [] # "@type mode: int" + + rna_struct= rna.rna_type + + # Operators and functions work differently + if func_type=='OPERATOR': + rna_func_name = rna_struct.identifier + rna_func_desc = rna_struct.description + items = rna_struct.properties.items() + else: + rna_func_name = rna.identifier + rna_func_desc = rna.description + items = rna.parameters.items() + + for rna_prop_identifier, rna_prop in items: + if rna_prop_identifier=='rna_type': + continue + + # clear vars + val = val_error = val_str = rna_prop_type = None + + # ['rna_type', 'name', 'array_length', 'description', 'hard_max', 'hard_min', 'identifier', 'precision', 'readonly', 'soft_max', 'soft_min', 'step', 'subtype', 'type'] + #rna_prop= op_rna.rna_type.properties[attr] + rna_prop_type = rna_prop.type.lower() # enum, float, int, boolean + + + try: length = rna_prop.array_length + except: length = 0 + + array_str = get_array_str(length) + + kw_type_str= "@type %s: %s%s" % (rna_prop_identifier, rna_prop_type, array_str) + kw_param_str= "@param %s: %s" % (rna_prop_identifier, rna_prop.description) + kw_param_set = False + + if func_type=='OPERATOR': + try: + val = getattr(rna, rna_prop_identifier) + val_error = False + except: + val = "'<UNDEFINED>'" + val_error = True + + + if val_error: + val_str = val + elif rna_prop_type=='float': + if length==0: + val_str= '%g' % val + if '.' not in val_str: + val_str += '.0' + else: + # array + val_str = str(tuple(val)) + + kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max))) + kw_param_set= True + + elif rna_prop_type=='int': + if length==0: + val_str='%d' % val + else: + val_str = str(tuple(val)) + + # print(dir(rna_prop)) + kw_param_str += (' in (%s, %s)' % (range_str(rna_prop.hard_min), range_str(rna_prop.hard_max))) + # These strings dont have a max length??? + #kw_param_str += ' (maximum length of %s)' % (rna_prop.max_length) + kw_param_set= True + + elif rna_prop_type=='boolean': + if length==0: + if val: val_str='True' + else: val_str='False' + else: + val_str = str(tuple(val)) + + elif rna_prop_type=='enum': + # no array here? + val_str="'%s'" % val + # Too cramped + kw_param_str += (' in (%s)' % ', '.join(rna_prop.items.keys())) + + kw_param_set= True + + elif rna_prop_type=='string': + # no array here? + val_str='"%s"' % val + + # todo - collection - array + # print (rna_prop.type) + + kw_args.append('%s = %s' % (rna_prop_identifier, val_str)) + + # stora + else: + # currently functions dont have a default value + kw_args.append('%s' % (rna_prop_identifier)) + + + # Same for operators and functions + kw_arg_attrs.append(kw_type_str) + if kw_param_set: + kw_arg_attrs.append(kw_param_str) + + + + out.write(ident+'def %s(%s):\n' % (rna_func_name, ', '.join(kw_args))) + out.write(ident+'\t"""\n') + out.write(ident+'\t%s\n' % rna_func_desc) + for desc in kw_arg_attrs: + out.write(ident+'\t%s\n' % desc) + out.write(ident+'\t@rtype: None\n') + out.write(ident+'\t"""\n') + + + +def rna2epy(target_path): + + # Use for faster lookups + # use rna_struct.identifier as the key for each dict + rna_full_path_dict = {} # store the result of full_rna_struct_path(rna_struct) + rna_children_dict = {} # store all rna_structs nested from here + rna_references_dict = {} # store a list of rna path strings that reference this type + rna_functions_dict = {} # store all functions directly in this type (not inherited) + rna_words = set() + + # def write_func(rna_func, ident): + + + def write_struct(rna_struct, ident): + identifier = rna_struct.identifier + + rna_base = rna_struct.base + + if rna_base: + out.write(ident+ 'class %s(%s):\n' % (identifier, rna_base.identifier)) + else: + out.write(ident+ 'class %s:\n' % identifier) + + out.write(ident+ '\t"""\n') + + title = 'The %s Object' % rna_struct.name + description = rna_struct.description + out.write(ident+ '\t%s\n' % title) + out.write(ident+ '\t%s\n' % ('=' * len(title))) + out.write(ident+ '\t\t%s\n' % description) + rna_words.update(description.split()) + + + # For convenience, give a list of all places were used. + rna_refs= rna_references_dict[identifier] + + if rna_refs: + out.write(ident+ '\t\t\n') + out.write(ident+ '\t\tReferences\n') + out.write(ident+ '\t\t==========\n') + + for rna_ref_string in rna_refs: + out.write(ident+ '\t\t\t- L{%s}\n' % rna_ref_string) + + out.write(ident+ '\t\t\n') + + else: + out.write(ident+ '\t\t\n') + out.write(ident+ '\t\t(no references to this struct found)\n') + out.write(ident+ '\t\t\n') + + for rna_prop_identifier, rna_prop in rna_struct.properties.items(): + + if rna_prop_identifier=='RNA': + continue + + if rna_prop_identifier=='rna_type': + continue + + rna_desc = rna_prop.description + + if rna_desc: rna_words.update(rna_desc.split()) + if not rna_desc: rna_desc = rna_prop.name + if not rna_desc: rna_desc = 'Note - No documentation for this property!' + + rna_prop_type = rna_prop.type.lower() + + if rna_prop_type=='collection': collection_str = 'Collection of ' + else: collection_str = '' + + try: rna_prop_ptr = rna_prop.fixed_type + except: rna_prop_ptr = None + + try: length = rna_prop.array_length + except: length = 0 + + array_str = get_array_str(length) + + if rna_prop.editable: readonly_str = '' + else: readonly_str = ' (readonly)' + + if rna_prop_ptr: # Use the pointer type + out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) + out.write(ident+ '\t@type %s: %sL{%s}%s%s\n' % (rna_prop_identifier, collection_str, rna_prop_ptr.identifier, array_str, readonly_str)) + else: + if rna_prop_type == 'enum': + if 0: + out.write(ident+ '\t@ivar %s: %s in (%s)\n' % (rna_prop_identifier, rna_desc, ', '.join(rna_prop.items.keys()))) + else: + out.write(ident+ '\t@ivar %s: %s in...\n' % (rna_prop_identifier, rna_desc)) + for e in rna_prop.items.keys(): + out.write(ident+ '\t\t- %s\n' % e) + + out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str)) + elif rna_prop_type == 'int' or rna_prop_type == 'float': + out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) + 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) )) + elif rna_prop_type == 'string': + out.write(ident+ '\t@ivar %s: %s (maximum length of %s)\n' % (rna_prop_identifier, rna_desc, rna_prop.max_length)) + out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str)) + else: + out.write(ident+ '\t@ivar %s: %s\n' % (rna_prop_identifier, rna_desc)) + out.write(ident+ '\t@type %s: %s%s%s\n' % (rna_prop_identifier, rna_prop_type, array_str, readonly_str)) + + + out.write(ident+ '\t"""\n\n') + + + # Write functions + # for rna_func in rna_struct.functions: # Better ignore inherited (line below) + for rna_func in rna_functions_dict[identifier]: + write_func(rna_func, ident+'\t', out, 'FUNCTION') + + out.write('\n') + + # Now write children recursively + for child in rna_children_dict[identifier]: + write_struct(child, ident + '\t') + + out = open(target_path, 'w') + + def base_id(rna_struct): + try: return rna_struct.base.identifier + except: return '' # invalid id + + #structs = [(base_id(rna_struct), rna_struct.identifier, rna_struct) for rna_struct in bpy.doc.structs.values()] + ''' + structs = [] + for rna_struct in bpy.doc.structs.values(): + structs.append( (base_id(rna_struct), rna_struct.identifier, rna_struct) ) + ''' + structs = [] + for rna_type_name in dir(bpy.types): + rna_type = getattr(bpy.types, rna_type_name) + if hasattr(rna_type, '__rna__'): + #if not rna_type_name.startswith('__'): + rna_struct = rna_type.__rna__ + identifier = rna_struct.identifier + structs.append( (base_id(rna_struct), identifier, rna_struct) ) + + + + # Store full rna path 'GameObjectSettings' -> 'Object.GameObjectSettings' + rna_full_path_dict[identifier] = full_rna_struct_path(rna_struct) + + # Store a list of functions, remove inherited later + rna_functions_dict[identifier]= list(rna_struct.functions) + + + # fill in these later + rna_children_dict[identifier]= [] + rna_references_dict[identifier]= [] + + + else: + print("Ignoring", rna_type_name) + + + # Sucks but we need to copy this so we can check original parent functions + rna_functions_dict__copy = {} + for key, val in rna_functions_dict.items(): + rna_functions_dict__copy[key] = val[:] + + + structs.sort() # not needed but speeds up sort below, setting items without an inheritance first + + # Arrange so classes are always defined in the correct order + deps_ok = False + while deps_ok == False: + deps_ok = True + rna_done = set() + + for i, (rna_base, identifier, rna_struct) in enumerate(structs): + + rna_done.add(identifier) + + if rna_base and rna_base not in rna_done: + deps_ok = False + data = structs.pop(i) + ok = False + while i < len(structs): + if structs[i][1]==rna_base: + structs.insert(i+1, data) # insert after the item we depend on. + ok = True + break + i+=1 + + if not ok: + print('Dependancy "%s" could not be found for "%s"' % (identifier, rna_base)) + + break + + # Done ordering structs + + + # precalc vars to avoid a lot of looping + for (rna_base, identifier, rna_struct) in structs: + + + # rna_struct_path = full_rna_struct_path(rna_struct) + rna_struct_path = rna_full_path_dict[identifier] + + for rna_prop_identifier, rna_prop in rna_struct.properties.items(): + if rna_prop_identifier=='RNA': + continue + + if rna_prop_identifier=='rna_type': + continue + + try: rna_prop_ptr = rna_prop.fixed_type + except: rna_prop_ptr = None + + # Does this property point to me? + if rna_prop_ptr: + rna_references_dict[rna_prop_ptr.identifier].append( "%s.%s" % (rna_struct_path, rna_prop_identifier) ) + + + + # Store nested children + nested = rna_struct.nested + if nested: + rna_children_dict[nested.identifier].append(rna_struct) + + + if rna_base: + rna_funcs = rna_functions_dict[identifier] + if rna_funcs: + # Remove inherited functions if we have any + rna_base_funcs = rna_functions_dict__copy[rna_base] + rna_funcs[:] = [f for f in rna_funcs if f not in rna_base_funcs] + + rna_functions_dict__copy.clear() + del rna_functions_dict__copy + + # Sort the refs, just reads nicer + for rna_refs in rna_references_dict.values(): + rna_refs.sort() + + for (rna_base, identifier, rna_struct) in structs: + if rna_struct.nested: + continue + + write_struct(rna_struct, '') + + + out.write('\n') + out.close() + + # # We could also just run.... + # os.system('epydoc source/blender/python/doc/rna.py -o ./source/blender/python/doc/html -v') + + + # Write graphviz + out= open(target_path.replace('.py', '.dot'), 'w') + out.write('digraph "rna data api" {\n') + out.write('\tnode [style=filled, shape = "box"];\n') + out.write('\toverlap=false;\n') + out.write('\trankdir = LR;\n') + out.write('\tsplines=true;\n') + out.write('\tratio=auto;\n') + + + + out.write('\tsize="10,10"\n') + #out.write('\tpage="8.5,11"\n') + #out.write('\tcenter=""\n') + + def isop(rna_struct): + return '_OT_' in rna_struct.identifier + + + for (rna_base, identifier, rna_struct) in structs: + if isop(rna_struct): + continue + + base = rna_struct.base + + + out.write('\t"%s";\n' % identifier) + + for (rna_base, identifier, rna_struct) in structs: + + if isop(rna_struct): + continue + + base = rna_struct.base + + if base and not isop(base): + out.write('\t"%s" -> "%s" [label="(base)" weight=1.0];\n' % (base.identifier, identifier)) + + nested = rna_struct.nested + if nested and not isop(nested): + out.write('\t"%s" -> "%s" [label="(nested)" weight=1.0];\n' % (nested.identifier, identifier)) + + + + rna_refs= rna_references_dict[identifier] + + for rna_ref_string in rna_refs: + + if '_OT_' in rna_ref_string: + continue + + ref = rna_ref_string.split('.')[-2] + out.write('\t"%s" -> "%s" [label="%s" weight=0.01];\n' % (ref, identifier, rna_ref_string)) + + + out.write('}\n') + out.close() + + # # We could also just run.... + # os.system('dot source/blender/python/doc/rna.dot -Tsvg -o ./source/blender/python/doc/rna.svg') + + + out= open(target_path.replace('.py', '.words'), 'w') + rna_words = list(rna_words) + rna_words.sort() + for w in rna_words: + out.write('%s\n' % w) + + +def op2epy(target_path): + out = open(target_path, 'w') + + operators = dir(bpy.ops) + operators.remove('add') + operators.remove('remove') + operators.sort() + + for op in operators: + + if op.startswith('__'): + continue + + # rna = getattr(bpy.types, op).__rna__ + rna = bpy.ops.__rna__(op) + + write_func(rna, '', out, 'OPERATOR') + + out.write('\n') + out.close() + +if __name__ == '__main__': + if 'bpy' not in dir(): + print("\nError, this script must run from inside blender2.5") + print(script_help_msg) + + else: + rna2epy('source/blender/python/doc/rna.py') + op2epy('source/blender/python/doc/bpyoperator.py') + + import sys + sys.exit() |