From 51f2dcd08ccde78223449383b98b9f7e35062b24 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 17 Nov 2009 12:21:41 +0000 Subject: [PATCH] - access console languages as modules rather then having the py operator call an operator - workaround for __getattr__ existing for types that dont support it --- release/scripts/op/console_python.py | 220 ++++++++++------------ release/scripts/op/console_shell.py | 93 +++------ release/scripts/ui/space_console.py | 36 ++-- source/blender/makesrna/intern/rna_main.c | 2 +- source/blender/python/intern/bpy_rna.c | 14 +- 5 files changed, 157 insertions(+), 208 deletions(-) diff --git a/release/scripts/op/console_python.py b/release/scripts/op/console_python.py index d3b745c7ead..ec700798b02 100644 --- a/release/scripts/op/console_python.py +++ b/release/scripts/op/console_python.py @@ -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 :| + try: + line = sc.history[-1].line + except: + return ('CANCELLED',) - def execute(self, context): - sc = context.space_data + if sc.console_type != 'PYTHON': + return ('CANCELLED',) - try: - line = sc.history[-1].line - except: - return ('CANCELLED',) + console, stdout, stderr = get_console(hash(context.region)) - if sc.console_type != 'PYTHON': - return ('CANCELLED',) + # Hack, useful but must add some other way to access + #if "C" not in console.locals: + console.locals["C"] = context - console, stdout, stderr = get_console(hash(context.region)) + # redirect output + sys.stdout = stdout + sys.stderr = stderr - # Hack, useful but must add some other way to access - #if "C" not in console.locals: - console.locals["C"] = context + # run the console + if not line.strip(): + line_exec = '\n' # executes a multiline statement + else: + line_exec = line - # redirect output - sys.stdout = stdout - sys.stderr = stderr + is_multiline = console.push(line_exec) - # run the console - if not line.strip(): - line_exec = '\n' # executes a multiline statement - else: - line_exec = line + stdout.seek(0) + stderr.seek(0) - is_multiline = console.push(line_exec) + output = stdout.read() + output_err = stderr.read() - stdout.seek(0) - stderr.seek(0) + # cleanup + sys.stdout = sys.__stdout__ + sys.stderr = sys.__stderr__ + sys.last_traceback = None - output = stdout.read() - output_err = stderr.read() + # So we can reuse, clear all data + stdout.truncate(0) + stderr.truncate(0) - # cleanup - sys.stdout = sys.__stdout__ - sys.stderr = sys.__stderr__ - sys.last_traceback = None + bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT') - # So we can reuse, clear all data - stdout.truncate(0) - stderr.truncate(0) + if is_multiline: + sc.prompt = PROMPT_MULTI + else: + sc.prompt = PROMPT - bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT') + # insert a new blank line + bpy.ops.console.history_append(text="", current_character=0, + remove_duplicates=True) - if is_multiline: - sc.prompt = self.PROMPT_MULTI - else: - sc.prompt = self.PROMPT + # 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') - # insert a new blank line - bpy.ops.console.history_append(text="", current_character=0, - remove_duplicates=True) - - # 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') - - return ('FINISHED',) + return ('FINISHED',) -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 +def autocomplete(context): + from console import intellisense - def poll(self, context): - return context.space_data.console_type == 'PYTHON' + sc = context.space_data - def execute(self, 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') - - context.area.tag_redraw() - - return ('FINISHED',) + return ('FINISHED',) -class PyConsoleBanner(bpy.types.Operator): - bl_idname = "console.banner_" + language_id +def banner(context): + sc = context.space_data + version_string = sys.version.strip().replace('\n', ' ') - def execute(self, 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 = 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 = PyConsoleExec.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',) diff --git a/release/scripts/op/console_shell.py b/release/scripts/op/console_shell.py index 874101d1c79..60768693030 100644 --- a/release/scripts/op/console_shell.py +++ b/release/scripts/op/console_shell.py @@ -41,73 +41,40 @@ def shell_run(text): add_scrollback(output, style) - -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',) +PROMPT = '$ ' -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 execute(context): + sc = context.space_data - def poll(self, context): - return context.space_data.console_type == 'PYTHON' - - def execute(self, context): - from console import intellisense - - sc = context.space_data - - # TODO + try: + line = sc.history[-1].line + except: 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 + 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) - return ('FINISHED',) + sc.prompt = os.getcwd()+PROMPT + return ('FINISHED',) + + +def autocomplete(context): + # sc = context.space_data + # TODO + return ('CANCELLED',) + + +def banner(context): + sc = context.space_data + + shell_run("bash --version") + sc.prompt = os.getcwd()+PROMPT + + return ('FINISHED',) -bpy.ops.add(ShellConsoleExec) -bpy.ops.add(ShellConsoleAutocomplete) -bpy.ops.add(ShellConsoleBanner) diff --git a/release/scripts/ui/space_console.py b/release/scripts/ui/space_console.py index e317c354596..689a6c4d4f0 100644 --- a/release/scripts/ui/space_console.py +++ b/release/scripts/ui/space_console.py @@ -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): diff --git a/source/blender/makesrna/intern/rna_main.c b/source/blender/makesrna/intern/rna_main.c index 1b93f6d1056..a068dc7cb26 100644 --- a/source/blender/makesrna/intern/rna_main.c +++ b/source/blender/makesrna/intern/rna_main.c @@ -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."}, diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index ddc8af117a6..e247b060088 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -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);