utility module for introspecting RNA for doc generation.
authorCampbell Barton <ideasman42@gmail.com>
Sat, 19 Dec 2009 13:48:50 +0000 (13:48 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 19 Dec 2009 13:48:50 +0000 (13:48 +0000)
release/scripts/modules/rna_info.py [new file with mode: 0644]

diff --git a/release/scripts/modules/rna_info.py b/release/scripts/modules/rna_info.py
new file mode 100644 (file)
index 0000000..8e1d16a
--- /dev/null
@@ -0,0 +1,355 @@
+# ##### 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.
+#
+# ##### END GPL LICENSE BLOCK #####
+
+# classes for extracting info from blenders internal classes
+
+import bpy
+
+
+class InfoStructRNA:
+    global_lookup = {}
+    def __init__(self, rna_type):
+        self.bl_rna = rna_type
+
+        self.identifier = rna_type.identifier
+        self.name = rna_type.name
+        self.description = rna_type.description.strip()
+
+        # set later
+        self.base = None
+        self.nested = None
+        self.full_path = ""
+
+        self.functions = []
+        self.children = []
+        self.references = []
+        self.properties = []
+
+    def build(self):
+        rna_type = self.bl_rna
+        parent_id = self.identifier
+        self.properties[:] = [GetInfoPropertyRNA(rna_prop, parent_id) for rna_prop in rna_type.properties.values()]
+        self.functions[:] = [GetInfoFunctionRNA(rna_prop, parent_id) for rna_prop in rna_type.functions.values()]
+
+    def getNestedProperties(self, ls = None):
+        if not ls:
+            ls = self.properties[:]
+
+        if self.nested:
+            self.nested.getNestedProperties(ls)
+
+        return ls
+
+    def __repr__(self):
+
+        txt = ''
+        txt += self.identifier
+        if self.base:
+            txt += '(%s)' % self.base.identifier
+        txt += ': ' + self.description + '\n'
+
+        for prop in self.properties:
+            txt += prop.__repr__() + '\n'
+
+        for func in self.functions:
+            txt += func.__repr__() + '\n'
+
+        return txt
+
+
+class InfoPropertyRNA:
+    global_lookup = {}
+    def __init__(self, rna_prop):
+        self.bl_prop = rna_prop
+        self.identifier = rna_prop.identifier
+        self.name = rna_prop.name
+        self.description = rna_prop.description.strip()
+
+    def build(self):
+        rna_prop = self.bl_prop
+
+        self.enum_items = []
+        self.min = -1
+        self.max = -1
+        self.array_length = getattr(rna_prop, "array_length", 0)
+
+        self.type = rna_prop.type.lower()
+        self.fixed_type = GetInfoStructRNA(rna_prop.fixed_type) # valid for pointer/collections
+        self.srna = GetInfoStructRNA(rna_prop.srna) # valid for pointer/collections
+
+    def __repr__(self):
+        txt = ''
+        txt += ' * ' + self.identifier + ': ' + self.description
+
+        return txt
+
+class InfoFunctionRNA:
+    global_lookup = {}
+    def __init__(self, rna_func):
+        self.bl_func = rna_func
+        self.identifier = rna_func.identifier
+        # self.name = rna_func.name # functions have no name!
+        self.description = rna_func.description.strip()
+
+        self.args = [] # todo
+        self.return_value = None # todo
+
+    def build(self):
+        rna_prop = self.bl_prop
+        pass
+
+    def __repr__(self):
+        txt = ''
+        txt += ' * ' + self.identifier + '('
+
+        for arg in self.args:
+            txt += arg.identifier + ', '
+        txt += '): ' + self.description
+        return txt
+
+
+def _GetInfoRNA(bl_rna, cls, parent_id=''):
+
+    if bl_rna == None:
+        return None
+
+    key = parent_id, bl_rna.identifier
+    try:
+        return cls.global_lookup[key]
+    except:
+        instance = cls.global_lookup[key] = cls(bl_rna)
+        return instance
+
+
+def GetInfoStructRNA(bl_rna):
+    return _GetInfoRNA(bl_rna, InfoStructRNA)
+
+def GetInfoPropertyRNA(bl_rna, parent_id):
+    return _GetInfoRNA(bl_rna, InfoPropertyRNA, parent_id)
+
+def GetInfoFunctionRNA(bl_rna, parent_id):
+    return _GetInfoRNA(bl_rna, InfoFunctionRNA, parent_id)
+
+
+def BuildRNAInfo():
+    # Use for faster lookups
+    # use rna_struct.identifier as the key for each dict
+    rna_struct_dict =          {}  # store identifier:rna lookups
+    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 rna_id_ignore(rna_id):
+        if rna_id == "rna_type":
+            return True
+
+        if "_OT_" in rna_id:
+            return True
+        if "_MT_" in rna_id:
+            return True
+        if "_PT_" in rna_id:
+            return True
+
+        return False
+
+    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_func, ident):
+    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)
+
+        try:           rna_struct = rna_type.bl_rna
+        except:        rna_struct = None
+
+        if rna_struct:
+            #if not rna_type_name.startswith('__'):
+
+            identifier = rna_struct.identifier
+
+            if not rna_id_ignore(identifier):
+                structs.append( (base_id(rna_struct), identifier, rna_struct) )
+
+                # Simple lookup
+                rna_struct_dict[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:
+
+        if rna_base:
+            rna_base_prop_keys = rna_struct_dict[rna_base].properties.keys() # could cache
+            rna_base_func_keys = [f.identifier for f in rna_struct_dict[rna_base].functions]
+        else:
+            rna_base_prop_keys = []
+            rna_base_func_keys= []
+
+        # 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_id_ignore(rna_prop_identifier):                     continue
+            if rna_prop_identifier in rna_base_prop_keys:      continue
+
+
+            for rna_prop_ptr in (getattr(rna_prop, "fixed_type", None), getattr(rna_prop, "srna", 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) )
+
+        for rna_func in rna_struct.functions:
+            for rna_prop_identifier, rna_prop in rna_func.parameters.items():
+
+                if rna_prop_identifier=='RNA':                                 continue
+                if rna_id_ignore(rna_prop_identifier):                 continue
+                if rna_prop_identifier in rna_base_func_keys:  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_func.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()
+
+
+    info_structs = []
+    for (rna_base, identifier, rna_struct) in structs:
+        #if rna_struct.nested:
+        #    continue
+
+        #write_struct(rna_struct, '')
+        info_struct= GetInfoStructRNA(rna_struct)
+        if rna_base:
+            info_struct.base = GetInfoStructRNA(rna_struct_dict[rna_base])
+        info_struct.nested = GetInfoStructRNA(rna_struct.nested)
+        info_struct.children[:] = rna_children_dict[identifier]
+        info_struct.references[:] = rna_references_dict[identifier]
+        info_struct.full_path = rna_full_path_dict[identifier]
+
+        info_structs.append(info_struct)
+
+    for rna_info_prop in InfoPropertyRNA.global_lookup.values():
+        rna_info_prop.build()
+
+    for rna_info_prop in InfoFunctionRNA.global_lookup.values():
+        rna_info_prop.build()
+
+    for rna_info in InfoStructRNA.global_lookup.values():
+        rna_info.build()
+
+    for rna_info in InfoStructRNA.global_lookup.values():
+        print(rna_info)
+
+    return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoPropertyRNA.global_lookup
+