- access console languages as modules rather then having the py operator call an...
authorCampbell Barton <ideasman42@gmail.com>
Tue, 17 Nov 2009 12:21:41 +0000 (12:21 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Tue, 17 Nov 2009 12:21:41 +0000 (12:21 +0000)
- workaround for __getattr__ existing for types that dont support it

release/scripts/op/console_python.py
release/scripts/op/console_shell.py
release/scripts/ui/space_console.py
source/blender/makesrna/intern/rna_main.c
source/blender/python/intern/bpy_rna.c

index d3b745c..ec70079 100644 (file)
@@ -66,160 +66,130 @@ def get_console(console_id):
     return console, stdout, stderr
 
 
-class PyConsoleExec(bpy.types.Operator):
-    '''Execute the current console line as a python expression.'''
-    bl_idname = "console.execute_" + language_id
-    bl_label = "Console Execute"
-    bl_register = False
+# Both prompts must be the same length
+PROMPT = '>>> '
+PROMPT_MULTI = '... '
 
-    # Both prompts must be the same length
-    PROMPT = '>>> '
-    PROMPT_MULTI = '... '
+def execute(context):
+    sc = context.space_data
 
-    # is this working???
-    '''
-    def poll(self, context):
-        return (context.space_data.type == 'PYTHON')
-    '''
-    # its not :|
-
-    def execute(self, context):
-        sc = context.space_data
-
-        try:
-            line = sc.history[-1].line
-        except:
-            return ('CANCELLED',)
-
-        if sc.console_type != 'PYTHON':
-            return ('CANCELLED',)
-
-        console, stdout, stderr = get_console(hash(context.region))
-
-        # Hack, useful but must add some other way to access
-        #if "C" not in console.locals:
-        console.locals["C"] = context
+    try:
+        line = sc.history[-1].line
+    except:
+        return ('CANCELLED',)
 
-        # redirect output
-        sys.stdout = stdout
-        sys.stderr = stderr
+    if sc.console_type != 'PYTHON':
+        return ('CANCELLED',)
 
-        # run the console
-        if not line.strip():
-            line_exec = '\n'  # executes a multiline statement
-        else:
-            line_exec = line
+    console, stdout, stderr = get_console(hash(context.region))
 
-        is_multiline = console.push(line_exec)
+    # Hack, useful but must add some other way to access
+    #if "C" not in console.locals:
+    console.locals["C"] = context
 
-        stdout.seek(0)
-        stderr.seek(0)
+    # redirect output
+    sys.stdout = stdout
+    sys.stderr = stderr
 
-        output = stdout.read()
-        output_err = stderr.read()
+    # run the console
+    if not line.strip():
+        line_exec = '\n'  # executes a multiline statement
+    else:
+        line_exec = line
 
-        # cleanup
-        sys.stdout = sys.__stdout__
-        sys.stderr = sys.__stderr__
-        sys.last_traceback = None
+    is_multiline = console.push(line_exec)
 
-        # So we can reuse, clear all data
-        stdout.truncate(0)
-        stderr.truncate(0)
+    stdout.seek(0)
+    stderr.seek(0)
 
-        bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
+    output = stdout.read()
+    output_err = stderr.read()
 
-        if is_multiline:
-            sc.prompt = self.PROMPT_MULTI
-        else:
-            sc.prompt = self.PROMPT
+    # cleanup
+    sys.stdout = sys.__stdout__
+    sys.stderr = sys.__stderr__
+    sys.last_traceback = None
 
-        # insert a new blank line
-        bpy.ops.console.history_append(text="", current_character=0,
-            remove_duplicates=True)
+    # So we can reuse, clear all data
+    stdout.truncate(0)
+    stderr.truncate(0)
 
-        # Insert the output into the editor
-        # not quite correct because the order might have changed,
-        # but ok 99% of the time.
-        if output:
-            add_scrollback(output, 'OUTPUT')
-        if output_err:
-            add_scrollback(output_err, 'ERROR')
+    bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
 
-        return ('FINISHED',)
+    if is_multiline:
+        sc.prompt = PROMPT_MULTI
+    else:
+        sc.prompt = PROMPT
 
+    # insert a new blank line
+    bpy.ops.console.history_append(text="", current_character=0,
+        remove_duplicates=True)
 
-class PyConsoleAutocomplete(bpy.types.Operator):
-    '''Evaluate the namespace up until the cursor and give a list of
-    options or complete the name if there is only one.'''
-    bl_idname = "console.autocomplete_" + language_id
-    bl_label = "Python Console Autocomplete"
-    bl_register = False
+    # Insert the output into the editor
+    # not quite correct because the order might have changed,
+    # but ok 99% of the time.
+    if output:
+        add_scrollback(output, 'OUTPUT')
+    if output_err:
+        add_scrollback(output_err, 'ERROR')
 
-    def poll(self, context):
-        return context.space_data.console_type == 'PYTHON'
+    return ('FINISHED',)
 
-    def execute(self, context):
-        from console import intellisense
 
-        sc = context.space_data
+def autocomplete(context):
+    from console import intellisense
 
-        console = get_console(hash(context.region))[0]
-        
-        current_line = sc.history[-1]
-        line = current_line.line
+    sc = context.space_data
 
-        if not console:
-            return ('CANCELLED',)
+    console = get_console(hash(context.region))[0]
+    
+    current_line = sc.history[-1]
+    line = current_line.line
 
-        if sc.console_type != 'PYTHON':
-            return ('CANCELLED',)
+    if not console:
+        return ('CANCELLED',)
 
-        # This function isnt aware of the text editor or being an operator
-        # just does the autocomp then copy its results back
-        current_line.line, current_line.current_character, scrollback = \
-            intellisense.expand(
-                line=current_line.line,
-                cursor=current_line.current_character,
-                namespace=console.locals,
-                private='-d' in sys.argv)
+    if sc.console_type != 'PYTHON':
+        return ('CANCELLED',)
 
-        # Now we need to copy back the line from blender back into the
-        # text editor. This will change when we dont use the text editor
-        # anymore
-        if scrollback:
-            add_scrollback(scrollback, 'INFO')
+    # This function isnt aware of the text editor or being an operator
+    # just does the autocomp then copy its results back
+    current_line.line, current_line.current_character, scrollback = \
+        intellisense.expand(
+            line=current_line.line,
+            cursor=current_line.current_character,
+            namespace=console.locals,
+            private='-d' in sys.argv)
 
-        context.area.tag_redraw()
+    # Now we need to copy back the line from blender back into the
+    # text editor. This will change when we dont use the text editor
+    # anymore
+    if scrollback:
+        add_scrollback(scrollback, 'INFO')
 
-        return ('FINISHED',)
+    context.area.tag_redraw()
 
+    return ('FINISHED',)
 
-class PyConsoleBanner(bpy.types.Operator):
-    bl_idname = "console.banner_" + language_id
 
-    def execute(self, context):
-        sc = context.space_data
-        version_string = sys.version.strip().replace('\n', ' ')
+def banner(context):
+    sc = context.space_data
+    version_string = sys.version.strip().replace('\n', ' ')
 
-        add_scrollback(" * Python Interactive Console %s *" % version_string, 'OUTPUT')
-        add_scrollback("Command History:  Up/Down Arrow", 'OUTPUT')
-        add_scrollback("Cursor:           Left/Right Home/End", 'OUTPUT')
-        add_scrollback("Remove:           Backspace/Delete", 'OUTPUT')
-        add_scrollback("Execute:          Enter", 'OUTPUT')
-        add_scrollback("Autocomplete:     Ctrl+Space", 'OUTPUT')
-        add_scrollback("Ctrl +/-  Wheel:  Zoom", 'OUTPUT')
-        add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, Mathutils, Geometry, BGL", 'OUTPUT')
-        add_scrollback("", 'OUTPUT')
-        add_scrollback("", 'OUTPUT')
-        sc.prompt = PyConsoleExec.PROMPT
+    add_scrollback(" * Python Interactive Console %s *" % version_string, 'OUTPUT')
+    add_scrollback("Command History:  Up/Down Arrow", 'OUTPUT')
+    add_scrollback("Cursor:           Left/Right Home/End", 'OUTPUT')
+    add_scrollback("Remove:           Backspace/Delete", 'OUTPUT')
+    add_scrollback("Execute:          Enter", 'OUTPUT')
+    add_scrollback("Autocomplete:     Ctrl+Space", 'OUTPUT')
+    add_scrollback("Ctrl +/-  Wheel:  Zoom", 'OUTPUT')
+    add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, Mathutils, Geometry, BGL", 'OUTPUT')
+    add_scrollback("", 'OUTPUT')
+    add_scrollback("", 'OUTPUT')
+    sc.prompt = PROMPT
 
-        # Add context into the namespace for quick access
-        console = get_console(hash(context.region))[0]
-        console.locals["C"] = bpy.context
+    # Add context into the namespace for quick access
+    console = get_console(hash(context.region))[0]
+    console.locals["C"] = bpy.context
 
-        return ('FINISHED',)
-
-bpy.ops.add(PyConsoleExec)
-bpy.ops.add(PyConsoleAutocomplete)
-bpy.ops.add(PyConsoleBanner)
+    return ('FINISHED',)
index 874101d..6076869 100644 (file)
@@ -41,73 +41,40 @@ def shell_run(text):
 
     add_scrollback(output, style)
 
+PROMPT = '$ '
 
-class ShellConsoleExec(bpy.types.Operator):
-    '''Execute the current console line as a python expression.'''
-    bl_idname = "console.execute_" + language_id
-    bl_label = "Console Execute"
-    bl_register = False
-
-    # Both prompts must be the same length
-    PROMPT = '$ '
-
-    # is this working???
-    '''
-    def poll(self, context):
-        return (context.space_data.type == 'PYTHON')
-    '''
-    # its not :|
-
-    def execute(self, context):
-        sc = context.space_data
-
-        try:
-            line = sc.history[-1].line
-        except:
-            return ('CANCELLED',)
-            
-        bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
-        
-        shell_run(line)
-        
-        # insert a new blank line
-        bpy.ops.console.history_append(text="", current_character=0,
-            remove_duplicates=True)
-
-        sc.prompt = os.getcwd()+ShellConsoleExec.PROMPT
-        return ('FINISHED',)
 
+def execute(context):
+    sc = context.space_data
 
-class ShellConsoleAutocomplete(bpy.types.Operator):
-    '''Evaluate the namespace up until the cursor and give a list of
-    options or complete the name if there is only one.'''
-    bl_idname = "console.autocomplete_" + language_id
-    bl_label = "Python Console Autocomplete"
-    bl_register = False
-
-    def poll(self, context):
-        return context.space_data.console_type == 'PYTHON'
+    try:
+        line = sc.history[-1].line
+    except:
+        return ('CANCELLED',)
+        
+    bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
+    
+    shell_run(line)
+    
+    # insert a new blank line
+    bpy.ops.console.history_append(text="", current_character=0,
+        remove_duplicates=True)
 
-    def execute(self, context):
-        from console import intellisense
+    sc.prompt = os.getcwd()+PROMPT
+    return ('FINISHED',)
 
-        sc = context.space_data
-        
-        # TODO
-        return ('CANCELLED',)
 
+def autocomplete(context):
+    # sc = context.space_data
+    # TODO
+    return ('CANCELLED',)
 
-class ShellConsoleBanner(bpy.types.Operator):
-    bl_idname = "console.banner_" + language_id
 
-    def execute(self, context):
-        sc = context.space_data
-        
-        shell_run("bash --version")
-        sc.prompt = os.getcwd()+ShellConsoleExec.PROMPT
+def banner(context):
+    sc = context.space_data
+    
+    shell_run("bash --version")
+    sc.prompt = os.getcwd()+PROMPT
 
-        return ('FINISHED',)
+    return ('FINISHED',)
 
-bpy.ops.add(ShellConsoleExec)
-bpy.ops.add(ShellConsoleAutocomplete)
-bpy.ops.add(ShellConsoleBanner)
index e317c35..689a6c4 100644 (file)
@@ -87,15 +87,16 @@ class CONSOLE_MT_language(bpy.types.Menu):
     bl_label = "Languages..."
 
     def draw(self, context):
+        import sys
+        
         layout = self.layout
         layout.column()
 
-        mod = bpy.ops.console
+        # Collect modules with 'console_*.execute'
         languages = []
-        for opname in dir(mod):
-            # execute_python, execute_shell etc.
-            if opname.startswith("execute_"):
-                languages.append(opname.split('_', 1)[-1])
+        for modname, mod in sys.modules.items():
+            if modname.startswith("console_") and hasattr(mod, "execute"):
+                languages.append(modname.split('_', 1)[-1])
 
         languages.sort()
 
@@ -118,14 +119,14 @@ class ConsoleExec(bpy.types.Operator):
     def execute(self, context):
         sc = context.space_data
 
-        execute = getattr(bpy.ops.console, "execute_" + sc.language, None)
+        module =  __import__("console_" + sc.language)
+        execute = getattr(module, "execute", None)
 
         if execute:
-            execute()
+            return execute(context)
         else:
             print("Error: bpy.ops.console.execute_" + sc.language + " - not found")
-
-        return ('FINISHED',)
+            return ('FINISHED',)
 
 
 class ConsoleAutocomplete(bpy.types.Operator):
@@ -140,15 +141,14 @@ class ConsoleAutocomplete(bpy.types.Operator):
 
     def execute(self, context):
         sc = context.space_data
-
-        autocomplete = getattr(bpy.ops.console, "autocomplete_" + sc.language, None)
+        module =  __import__("console_" + sc.language)
+        autocomplete = getattr(module, "autocomplete", None)
 
         if autocomplete:
-            autocomplete()
+            return autocomplete(context)
         else:
             print("Error: bpy.ops.console.autocomplete_" + sc.language + " - not found")
-
-        return ('FINISHED',)
+            return ('FINISHED',)
 
 
 class ConsoleBanner(bpy.types.Operator):
@@ -161,14 +161,14 @@ class ConsoleBanner(bpy.types.Operator):
         if not sc.language:
             sc.language = 'python'
 
-        banner = getattr(bpy.ops.console, "banner_" + sc.language, None)
+        module =  __import__("console_" + sc.language)
+        banner = getattr(module, "banner", None)
 
         if banner:
-            banner()
+            return banner(context)
         else:
             print("Error: bpy.ops.console.banner_" + sc.language + " - not found")
-
-        return ('FINISHED',)
+            return ('FINISHED',)
 
 
 class ConsoleLanguage(bpy.types.Operator):
index 1b93f6d..a068dc7 100644 (file)
@@ -258,7 +258,7 @@ void RNA_def_main(BlenderRNA *brna)
                {"brushes", "Brush", "rna_Main_brush_begin", "Brushes", "Brush datablocks."},
                {"worlds", "World", "rna_Main_world_begin", "Worlds", "World datablocks."},
                {"groups", "Group", "rna_Main_group_begin", "Groups", "Group datablocks."},
-               {"keys", "Key", "rna_Main_key_begin", "Keys", "Key datablocks."},
+/*             {"keys", "Key", "rna_Main_key_begin", "Keys", "Key datablocks."}, */
                {"scripts", "ID", "rna_Main_script_begin", "Scripts", "Script datablocks (DEPRECATED)."},
                {"texts", "Text", "rna_Main_text_begin", "Texts", "Text datablocks."},
                {"sounds", "Sound", "rna_Main_sound_begin", "Sounds", "Sound datablocks."},
index ddc8af1..e247b06 100644 (file)
@@ -1205,6 +1205,11 @@ static PyObject *pyrna_struct_subscript( BPy_StructRNA *self, PyObject *key )
        IDProperty *group, *idprop;
        char *name= _PyUnicode_AsString(key);
 
+       if(RNA_struct_idproperties_check(&self->ptr)==0) {
+               PyErr_SetString( PyExc_TypeError, "this type doesn't support IDProperties");
+               return NULL;
+       }
+
        if(name==NULL) {
                PyErr_SetString( PyExc_TypeError, "only strings are allowed as keys of ID properties");
                return NULL;
@@ -1512,7 +1517,14 @@ static PyObject *pyrna_struct_getattro( BPy_StructRNA *self, PyObject *pyname )
        FunctionRNA *func;
        
        if(name[0]=='_') { // rna can't start with a "_", so for __dict__ and similar we can skip using rna lookups
-               ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
+               /* annoying exception, maybe we need to have different types for this... */
+               if((strcmp(name, "__getitem__")==0 || strcmp(name, "__setitem__")==0) && !RNA_struct_idproperties_check(&self->ptr)) {
+                       PyErr_SetString(PyExc_AttributeError, "StructRNA - no __getitem__ support for this type");
+                       ret = NULL;
+               }
+               else {
+                       ret = PyObject_GenericGetAttr((PyObject *)self, pyname);
+               }
        }
        else if ((prop = RNA_struct_find_property(&self->ptr, name))) {
                ret = pyrna_prop_to_py(&self->ptr, prop);