Change to text3d: When back or front is enabled, the bevel rim on the other side...
[blender.git] / release / scripts / op / console_python.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import sys
21 import bpy
22
23 language_id = 'python'
24
25
26 def add_scrollback(text, text_type):
27     for l in text.split('\n'):
28         bpy.ops.console.scrollback_append(text=l.replace('\t', '    '),
29             type=text_type)
30
31
32 def get_console(console_id):
33     '''
34     helper function for console operators
35     currently each text datablock gets its own
36     console - code.InteractiveConsole()
37     ...which is stored in this function.
38
39     console_id can be any hashable type
40     '''
41     from code import InteractiveConsole
42
43     consoles = getattr(get_console, "consoles", None)
44
45     if consoles is None:
46         consoles = get_console.consoles = {}
47     else:
48         # check if clearning the namespace is needed to avoid a memory leak.
49         # the window manager is normally loaded with new blend files
50         # so this is a reasonable way to deal with namespace clearing.
51         # bpy.data hashing is reset by undo so cant be used.
52         hash_prev = getattr(get_console, "consoles_namespace_hash", 0)
53         hash_next = hash(bpy.context.manager)
54
55         if hash_prev != hash_next:
56             get_console.consoles_namespace_hash = hash_next
57             consoles.clear()
58
59     console_data = consoles.get(console_id)
60
61     if console_data:
62         console, stdout, stderr = console_data
63
64         # XXX, bug in python 3.1.2 ? (worked in 3.1.1)
65         # seems there is no way to clear StringIO objects for writing, have to make new ones each time.
66         import io
67         stdout = io.StringIO()
68         stderr = io.StringIO()
69     else:
70         namespace = {'__builtins__': __builtins__, 'bpy': bpy}
71         console = InteractiveConsole(locals=namespace, filename="<blender_console>")
72
73         import io
74         stdout = io.StringIO()
75         stderr = io.StringIO()
76
77         consoles[console_id] = console, stdout, stderr
78
79     return console, stdout, stderr
80
81
82 # Both prompts must be the same length
83 PROMPT = '>>> '
84 PROMPT_MULTI = '... '
85
86
87 def execute(context):
88     sc = context.space_data
89
90     try:
91         line_object = sc.history[-1]
92     except:
93         return {'CANCELLED'}
94
95     if sc.console_type != 'PYTHON':
96         return {'CANCELLED'}
97
98     console, stdout, stderr = get_console(hash(context.region))
99
100     # Hack, useful but must add some other way to access
101     #if "C" not in console.locals:
102     console.locals["C"] = context
103
104     # redirect output
105     sys.stdout = stdout
106     sys.stderr = stderr
107
108     # dont allow the stdin to be used, can lock blender.
109     stdin_backup = sys.stdin
110     sys.stdin = None
111
112     # incase exception happens
113     line = "" # incase of encodingf error
114     is_multiline = False
115
116     try:
117         line = line_object.line
118
119         # run the console, "\n" executes a multiline statement
120         line_exec = line if line.strip() else "\n"
121
122         is_multiline = console.push(line_exec)
123     except:
124         # unlikely, but this can happen with unicode errors for example.
125         import traceback
126         stderr.write(traceback.format_exc())
127
128
129     stdout.seek(0)
130     stderr.seek(0)
131
132     output = stdout.read()
133     output_err = stderr.read()
134
135     # cleanup
136     sys.stdout = sys.__stdout__
137     sys.stderr = sys.__stderr__
138     sys.last_traceback = None
139
140     # So we can reuse, clear all data
141     stdout.truncate(0)
142     stderr.truncate(0)
143
144     bpy.ops.console.scrollback_append(text=sc.prompt + line, type='INPUT')
145
146     if is_multiline:
147         sc.prompt = PROMPT_MULTI
148     else:
149         sc.prompt = PROMPT
150
151     # insert a new blank line
152     bpy.ops.console.history_append(text="", current_character=0,
153         remove_duplicates=True)
154
155     # Insert the output into the editor
156     # not quite correct because the order might have changed,
157     # but ok 99% of the time.
158     if output:
159         add_scrollback(output, 'OUTPUT')
160     if output_err:
161         add_scrollback(output_err, 'ERROR')
162
163     # restore the stdin
164     sys.stdin = stdin_backup
165
166     return {'FINISHED'}
167
168
169 def autocomplete(context):
170     from console import intellisense
171
172     sc = context.space_data
173
174     console = get_console(hash(context.region))[0]
175
176     if not console:
177         return {'CANCELLED'}
178
179     if sc.console_type != 'PYTHON':
180         return {'CANCELLED'}
181
182     # dont allow the stdin to be used, can lock blender.
183     # note: unlikely stdin would be used for autocomp. but its possible.
184     stdin_backup = sys.stdin
185     sys.stdin = None
186
187     scrollback = ""
188     scrollback_error = ""
189
190     try:
191         current_line = sc.history[-1]
192         line = current_line.line
193
194         # This function isnt aware of the text editor or being an operator
195         # just does the autocomp then copy its results back
196         current_line.line, current_line.current_character, scrollback = \
197             intellisense.expand(
198                 line=current_line.line,
199                 cursor=current_line.current_character,
200                 namespace=console.locals,
201                 private=bpy.app.debug)
202     except:
203         # unlikely, but this can happen with unicode errors for example.
204         # or if the api attribute access its self causes an error.
205         import traceback
206         scrollback_error = traceback.format_exc()
207
208     # Separate automplete output by command prompts
209     if scrollback != '':
210         bpy.ops.console.scrollback_append(text=sc.prompt + current_line.line, type='INPUT')
211
212     # Now we need to copy back the line from blender back into the
213     # text editor. This will change when we dont use the text editor
214     # anymore
215     if scrollback:
216         add_scrollback(scrollback, 'INFO')
217
218     if scrollback_error:
219         add_scrollback(scrollback_error, 'ERROR')
220
221     # restore the stdin
222     sys.stdin = stdin_backup
223
224     context.area.tag_redraw()
225
226     return {'FINISHED'}
227
228
229 def banner(context):
230     sc = context.space_data
231     version_string = sys.version.strip().replace('\n', ' ')
232
233     add_scrollback(" * Python Interactive Console %s *" % version_string, 'OUTPUT')
234     add_scrollback("Command History:  Up/Down Arrow", 'OUTPUT')
235     add_scrollback("Cursor:           Left/Right Home/End", 'OUTPUT')
236     add_scrollback("Remove:           Backspace/Delete", 'OUTPUT')
237     add_scrollback("Execute:          Enter", 'OUTPUT')
238     add_scrollback("Autocomplete:     Ctrl+Space", 'OUTPUT')
239     add_scrollback("Ctrl +/-  Wheel:  Zoom", 'OUTPUT')
240     add_scrollback("Builtin Modules: bpy, bpy.data, bpy.ops, bpy.props, bpy.types, bpy.context, bgl, blf, mathutils, geometry", 'OUTPUT')
241     add_scrollback("", 'OUTPUT')
242     add_scrollback("  WARNING!!! Blender 2.5 API is subject to change, see API reference for more info.", 'ERROR')
243     add_scrollback("", 'OUTPUT')
244     sc.prompt = PROMPT
245
246     # Add context into the namespace for quick access
247     console = get_console(hash(context.region))[0]
248     console.locals["C"] = bpy.context
249
250     return {'FINISHED'}
251
252
253 def register():
254     pass
255
256
257 def unregister():
258     pass
259
260
261 if __name__ == "__main__":
262     register()