update rna_info and rna_rna for better introspection
authorCampbell Barton <ideasman42@gmail.com>
Fri, 25 Dec 2009 14:42:00 +0000 (14:42 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Fri, 25 Dec 2009 14:42:00 +0000 (14:42 +0000)
release/scripts/modules/bpy_types.py
release/scripts/modules/rna_info.py
source/blender/makesrna/intern/rna_rna.c
source/blender/python/epy_doc_gen.py

index 2351fa8b09136ce91fdf3532a124e9df0ec4a3fe..15bf71e017ed5c6a57fdf89f718ee5e604423170 100644 (file)
@@ -42,25 +42,27 @@ class Object(bpy_types.ID):
 
     @property
     def children(self):
+        """All the children of this object"""
         import bpy
         return [child for child in bpy.data.objects if child.parent == self]
 
 
 class _GenericBone:
-    '''
+    """
     functions for bones, common between Armature/Pose/Edit bones.
     internal subclassing use only.
-    '''
+    """
     __slots__ = ()
 
     def translate(self, vec):
+        """Utility function to add *vec* to the head and tail of this bone."""
         self.head += vec
         self.tail += vec
 
     def parent_index(self, parent_test):
-        '''
+        """
         The same as 'bone in other_bone.parent_recursive' but saved generating a list.
-        '''
+        """
         # use the name so different types can be tested.
         name = parent_test.name
 
@@ -76,11 +78,13 @@ class _GenericBone:
 
     @property
     def basename(self):
+        """The name of this bone before any '.' character"""
         #return self.name.rsplit(".", 1)[0]
         return self.name.split(".")[0]
 
     @property
     def parent_recursive(self):
+        """A list of parents, starting with the immediate parent"""
         parent_list = []
         parent = self.parent
 
@@ -94,23 +98,26 @@ class _GenericBone:
 
     @property
     def length(self):
+        """The distance from head to tail, when set the head is moved to fit the length."""
         return self.vector.length
 
     @length.setter
     def length(self, value):
-        """The distance from head to tail"""
         self.tail = self.head + ((self.tail - self.head).normalize() * value)
 
     @property
     def vector(self):
+        """The direction this bone is pointing. Utility function for (tail - head)"""
         return (self.tail - self.head)
 
     @property
     def children(self):
+        """A list of all the bones children."""
         return [child for child in self._other_bones if child.parent == self]
 
     @property
     def children_recursive(self):
+        """a list of all children from this bone."""
         bones_children = []
         for bone in self._other_bones:
             index = bone.parent_index(self)
@@ -123,10 +130,11 @@ class _GenericBone:
 
     @property
     def children_recursive_basename(self):
-        '''
+        """
         Returns a chain of children with the same base name as this bone
-        Only direct chains are supported, forks caused by multiple children with matching basenames will.
-        '''
+        Only direct chains are supported, forks caused by multiple children with matching basenames will
+        terminate the function and not be returned.
+        """
         basename = self.basename
         chain = []
 
@@ -177,10 +185,10 @@ class EditBone(StructRNA, _GenericBone):
     __slots__ = ()
 
     def align_orientation(self, other):
-        '''
+        """
         Align this bone to another by moving its tail and settings its roll
         the length of the other bone is not used.
-        '''
+        """
         vec = other.vector.normalize() * self.length
         self.tail = self.head + vec
         self.roll = other.roll
@@ -196,10 +204,10 @@ class Mesh(bpy_types.ID):
     __slots__ = ()
 
     def from_pydata(self, verts, edges, faces):
-        '''
+        """
         Make a mesh from a list of verts/edges/faces
         Until we have a nicer way to make geometry, use this.
-        '''
+        """
         self.add_geometry(len(verts), len(edges), len(faces))
 
         verts_flat = [f for v in verts for f in v]
@@ -244,7 +252,7 @@ class Mesh(bpy_types.ID):
         return [edge_face_count_dict.get(ed.key, 0) for ed in mesh.edges]
 
     def edge_loops(self, faces=None, seams=()):
-        '''
+        """
         Edge loops defined by faces
 
         Takes me.faces or a list of faces and returns the edge loops
@@ -255,7 +263,7 @@ class Mesh(bpy_types.ID):
         [ [(0,1), (4, 8), (3,8)], ...]
 
         optionaly, seams are edge keys that will be removed
-        '''
+        """
 
         OTHER_INDEX = 2,3,0,1 # opposite face index
 
@@ -396,9 +404,9 @@ class Menu(StructRNA):
             layout.operator(operator, text=bpy.utils.display_name(f)).path = path
 
     def draw_preset(self, context):
-        '''Define these on the subclass
+        """Define these on the subclass
          - preset_operator
          - preset_subdir
-        '''
+        """
         import bpy
         self.path_menu(bpy.utils.preset_paths(self.preset_subdir), self.preset_operator)
index abb7bb35af0280d554989e4465a2beeed0e09690..2a70feec9ddeeaed3de024b8d49156e48017586b 100644 (file)
@@ -20,6 +20,9 @@
 
 import bpy
 
+# use to strip python paths
+script_paths = bpy.utils.script_paths()
+
 def range_str(val):
     if val < -10000000:        return '-inf'
     if val >  10000000:        return 'inf'
@@ -28,6 +31,12 @@ def range_str(val):
     else:
         return str(val)
 
+def float_as_string(f):
+    val_str = "%g" % f
+    if '.' not in val_str and '-' not in val_str: # value could be 1e-05
+        val_str += '.0'
+    return val_str
+
 class InfoStructRNA:
     global_lookup = {}
     def __init__(self, rna_type):
@@ -73,6 +82,31 @@ class InfoStructRNA:
 
         return ls
 
+    def _get_py_visible_attrs(self):
+        attrs = []
+        py_class = getattr(bpy.types, self.identifier)
+        for attr_str in dir(py_class):
+            if attr_str.startswith("_"):
+                continue                
+            attrs.append((attr_str, getattr(py_class, attr_str)))
+        return attrs
+        
+
+    def get_py_properties(self):
+        properties = []
+        for identifier, attr in self._get_py_visible_attrs():
+            if type(attr) is property:
+                properties.append((identifier, attr))
+        return properties
+    
+    def get_py_functions(self):
+        import types
+        functions = []
+        for identifier, attr in self._get_py_visible_attrs():
+            if type(attr) is types.FunctionType:
+                functions.append((identifier, attr))
+        return functions
+    
     def __repr__(self):
 
         txt = ''
@@ -106,6 +140,10 @@ class InfoPropertyRNA:
         self.min = getattr(rna_prop, "hard_min", -1)
         self.max = getattr(rna_prop, "hard_max", -1)
         self.array_length = getattr(rna_prop, "array_length", 0)
+        self.collection_type = GetInfoStructRNA(rna_prop.srna)
+        self.is_required = rna_prop.is_required
+        self.is_readonly = rna_prop.is_readonly
+        self.is_never_none = rna_prop.is_never_none
 
         self.type = rna_prop.type.lower()
         fixed_type = getattr(rna_prop, "fixed_type", "")
@@ -118,12 +156,43 @@ class InfoPropertyRNA:
             self.enum_items[:] = rna_prop.items.keys()
 
         if self.array_length:
-            self.default_str = str(getattr(rna_prop, "default_array", ""))
+            self.default = tuple(getattr(rna_prop, "default_array", ()))
+            self.default_str = ''
+            # special case for floats
+            if len(self.default) > 0:
+                if type(self.default[0]) is float:
+                    self.default_str = "(%s)" % ", ".join([float_as_string(f) for f in self.default])
+            if not self.default_str:
+                self.default_str = str(self.default)
         else:
-            self.default_str = str(getattr(rna_prop, "default", ""))
+            self.default = getattr(rna_prop, "default", "")
+            if type(self.default) is float:
+                self.default_str = float_as_string(self.default)
+            else:
+                self.default_str = str(self.default)
 
         self.srna = GetInfoStructRNA(rna_prop.srna) # valid for pointer/collections
-
+    
+    def get_default_string(self):
+        # pointer has no default, just set as None
+        if self.type == "pointer":
+           return "None"
+        elif self.type == "string":
+           return '"' + self.default_str + '"'
+        elif self.type == "enum":
+            if self.default_str:
+               return "'" + self.default_str + "'"
+            else:
+                return ""
+
+        return self.default_str
+    
+    def get_arg_default(self, force=True):
+        default = self.get_default_string()
+        if default and (force or self.is_required == False):
+            return "%s=%s" % (self.identifier, default)
+        return self.identifier
+    
     def __repr__(self):
         txt = ''
         txt += ' * ' + self.identifier + ': ' + self.description
@@ -162,6 +231,65 @@ class InfoFunctionRNA:
         return txt
 
 
+class InfoOperatorRNA:
+    global_lookup = {}
+    def __init__(self, rna_op):
+        self.bl_op = rna_op
+        self.identifier = rna_op.identifier
+        
+        mod, name = self.identifier.split("_OT_", 1)
+        self.module_name = mod.lower()
+        self.func_name = name
+        
+        # self.name = rna_func.name # functions have no name!
+        self.description = rna_op.description.strip()
+
+        self.args = []
+
+    def build(self):
+        rna_op = self.bl_op
+        parent_id = self.identifier
+        for rna_id, rna_prop in rna_op.properties.items():
+            if rna_id == "rna_type":
+                continue
+            
+            prop = GetInfoPropertyRNA(rna_prop, parent_id)
+            self.args.append(prop)
+
+    def get_location(self):
+        op_class = getattr(bpy.types, self.identifier)
+        op_func = getattr(op_class, "execute", None)
+        if op_func is None:
+            op_func = getattr(op_class, "invoke", None)
+        if op_func is None:
+            op_func = getattr(op_class, "poll", None)
+
+        if op_func:
+            op_code = op_func.__code__
+            source_path = op_code.co_filename
+
+            # clear the prefix
+            for p in script_paths:
+                source_path = source_path.split(p)[-1]
+            
+            if source_path[0] in "/\\":
+                source_path= source_path[1:]
+            
+            return source_path, op_code.co_firstlineno
+        else:
+            return None, None
+
+'''
+    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:
@@ -184,6 +312,8 @@ def GetInfoPropertyRNA(bl_rna, parent_id):
 def GetInfoFunctionRNA(bl_rna, parent_id):
     return _GetInfoRNA(bl_rna, InfoFunctionRNA, parent_id)
 
+def GetInfoOperatorRNA(bl_rna):
+    return _GetInfoRNA(bl_rna, InfoOperatorRNA)
 
 def BuildRNAInfo():
     # Use for faster lookups
@@ -205,7 +335,8 @@ def BuildRNAInfo():
             return True
         if "_PT_" in rna_id:
             return True
-
+        if "_HT_" in rna_id:
+            return True
         return False
 
     def full_rna_struct_path(rna_struct):
@@ -395,9 +526,34 @@ def BuildRNAInfo():
             if func.return_value:
                 func.return_value.build()
                 
-        
+    # now for operators
+    op_mods = dir(bpy.ops)
+
+    for op_mod_name in sorted(op_mods):
+        if op_mod_name.startswith('__') or op_mod_name in ("add", "remove"):
+            continue
+
+        op_mod = getattr(bpy.ops, op_mod_name)
+        operators = dir(op_mod)
+        for op in sorted(operators):
+            try:
+                rna_prop = getattr(op_mod, op).get_rna()
+            except AttributeError:
+                rna_prop = None
+            except TypeError:
+                rna_prop = None
+
+            if rna_prop:
+                GetInfoOperatorRNA(rna_prop.bl_rna)
+
+    for rna_info in InfoOperatorRNA.global_lookup.values():
+        rna_info.build()
+        for rna_prop in rna_info.args:
+            rna_prop.build()
+
+    
     #for rna_info in InfoStructRNA.global_lookup.values():
     #    print(rna_info)
 
-    return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoPropertyRNA.global_lookup
+    return InfoStructRNA.global_lookup, InfoFunctionRNA.global_lookup, InfoOperatorRNA.global_lookup, InfoPropertyRNA.global_lookup
 
index 5fd87728fb86971961cfffc9be3ca18878deec26..e6c21d8fedeaa9b3af4d23e8f27fa5aa287c2663 100644 (file)
@@ -425,7 +425,7 @@ static int rna_Property_unit_get(PointerRNA *ptr)
        return RNA_SUBTYPE_UNIT(prop->subtype);
 }
 
-static int rna_Property_editable_get(PointerRNA *ptr)
+static int rna_Property_readonly_get(PointerRNA *ptr)
 {
        PropertyRNA *prop= (PropertyRNA*)ptr->data;
 
@@ -433,7 +433,7 @@ static int rna_Property_editable_get(PointerRNA *ptr)
         * data for introspection we only need to know if it can be edited so the
         * flag is better for this */
 //     return RNA_property_editable(ptr, prop);
-       return prop->flag & PROP_EDITABLE ? 1:0;
+       return prop->flag & PROP_EDITABLE ? 0:1;
 }
 
 static int rna_Property_use_return_get(PointerRNA *ptr)
@@ -442,6 +442,18 @@ static int rna_Property_use_return_get(PointerRNA *ptr)
        return prop->flag & PROP_RETURN ? 1:0;
 }
 
+static int rna_Property_is_required_get(PointerRNA *ptr)
+{
+       PropertyRNA *prop= (PropertyRNA*)ptr->data;
+       return prop->flag & PROP_REQUIRED ? 1:0;
+}
+
+static int rna_Property_is_never_none_get(PointerRNA *ptr)
+{
+       PropertyRNA *prop= (PropertyRNA*)ptr->data;
+       return prop->flag & PROP_NEVER_NULL ? 1:0;
+}
+
 static int rna_Property_array_length_get(PointerRNA *ptr)
 {
        PropertyRNA *prop= (PropertyRNA*)ptr->data;
@@ -934,15 +946,25 @@ static void rna_def_property(BlenderRNA *brna)
        RNA_def_property_enum_funcs(prop, "rna_Property_unit_get", NULL, NULL);
        RNA_def_property_ui_text(prop, "Unit", "Type of units for this property.");
 
-       prop= RNA_def_property(srna, "editable", PROP_BOOLEAN, PROP_NONE);
+       prop= RNA_def_property(srna, "is_readonly", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_boolean_funcs(prop, "rna_Property_readonly_get", NULL);
+       RNA_def_property_ui_text(prop, "Read Only", "Property is editable through RNA.");
+
+       prop= RNA_def_property(srna, "is_required", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_boolean_funcs(prop, "rna_Property_is_required_get", NULL);
+       RNA_def_property_ui_text(prop, "Required", "False when this property is an optional argument in an rna function.");
+
+       prop= RNA_def_property(srna, "is_never_none", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
-       RNA_def_property_boolean_funcs(prop, "rna_Property_editable_get", NULL);
-       RNA_def_property_ui_text(prop, "Editable", "Property is editable through RNA.");
+       RNA_def_property_boolean_funcs(prop, "rna_Property_is_never_none_get", NULL);
+       RNA_def_property_ui_text(prop, "Never None", "True when this value can't be set to None.");
 
        prop= RNA_def_property(srna, "use_return", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
        RNA_def_property_boolean_funcs(prop, "rna_Property_use_return_get", NULL);
-       RNA_def_property_ui_text(prop, "Return", "True when this property is a return value from an rna function..");
+       RNA_def_property_ui_text(prop, "Return", "True when this property is a return value from an rna function.");
 
        prop= RNA_def_property(srna, "registered", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_clear_flag(prop, PROP_EDITABLE);
index 8783cec93abc84310da5498b3ada52aa4f6a384c..7e32b29aa1f5e1ddec5ea1973c63c6d63b475486 100644 (file)
@@ -397,8 +397,8 @@ def rna2epy(BASEPATH):
 
             array_str = get_array_str(length)
 
-            if rna_prop.editable:      readonly_str = ''
-            else:                              readonly_str = ' (readonly)'
+            if rna_prop.is_readonly:   readonly_str = ' (readonly)'
+            else:                              readonly_str = ''
 
             if rna_prop_ptr: # Use the pointer type
                 out.write(ident+ '\t@ivar %s: %s\n' %  (rna_prop_identifier, rna_desc))