~ubuntu-branches/debian/experimental/spyder/experimental

« back to all changes in this revision

Viewing changes to spyderlib/widgets/internalshell.py

  • Committer: Package Import Robot
  • Author(s): Picca Frédéric-Emmanuel
  • Date: 2013-02-27 09:51:28 UTC
  • mfrom: (1.1.18)
  • Revision ID: package-import@ubuntu.com-20130227095128-wtx1irpvf4vl79lj
Tags: 2.2.0~beta3+dfsg-1
* Imported Upstream version 2.2.0~beta3+dfsg
* debian /patches
  - 0002-feature-forwarded-add-icon-to-desktop-file.patch (deleted)
    this patch was integrated by the upstream.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
#
3
 
# Copyright © 2009-2010 Pierre Raybaut
4
 
# Licensed under the terms of the MIT License
5
 
# (see spyderlib/__init__.py for details)
6
 
 
7
 
"""Internal shell widget : PythonShellWidget + Interpreter"""
8
 
 
9
 
# pylint: disable=C0103
10
 
# pylint: disable=R0903
11
 
# pylint: disable=R0911
12
 
# pylint: disable=R0201
13
 
 
14
 
#FIXME: Internal shell MT: for i in range(100000): print i -> bug
15
 
 
16
 
#----Builtins
17
 
import __builtin__
18
 
from spyderlib.widgets.objecteditor import oedit
19
 
__builtin__.oedit = oedit
20
 
 
21
 
import os
22
 
import threading
23
 
from time import time
24
 
from subprocess import Popen
25
 
 
26
 
from spyderlib.qt.QtGui import QMessageBox
27
 
from spyderlib.qt.QtCore import SIGNAL, QObject, QEventLoop
28
 
 
29
 
# Local import
30
 
from spyderlib import get_versions
31
 
from spyderlib.utils.qthelpers import create_action, get_std_icon
32
 
from spyderlib.interpreter import Interpreter
33
 
from spyderlib.utils.dochelpers import getargtxt, getsource, getdoc, getobjdir
34
 
from spyderlib.utils.misc import get_error_match
35
 
#TODO: remove the CONF object and make it work anyway
36
 
# In fact, this 'CONF' object has nothing to do in package spyderlib.widgets
37
 
# which should not contain anything directly related to Spyder's main app
38
 
from spyderlib.baseconfig import get_conf_path, _
39
 
from spyderlib.config import CONF
40
 
from spyderlib.widgets.shell import PythonShellWidget
41
 
 
42
 
 
43
 
def create_banner(message):
44
 
    """Create internal shell banner"""
45
 
    versions = get_versions()
46
 
    return 'Spyder %s on Python %s [%s]\n%s' % (
47
 
        versions['spyder'], versions['python'], versions['system'], message)
48
 
 
49
 
 
50
 
class SysOutput(QObject):
51
 
    """Handle standard I/O queue"""
52
 
    def __init__(self):
53
 
        QObject.__init__(self)
54
 
        self.queue = []
55
 
        self.lock = threading.Lock()
56
 
        
57
 
    def write(self, val):
58
 
        self.lock.acquire()
59
 
        self.queue.append(val)
60
 
        self.lock.release()
61
 
        self.emit(SIGNAL("void data_avail()"))
62
 
 
63
 
    def empty_queue(self):
64
 
        self.lock.acquire()
65
 
        s = "".join(self.queue)
66
 
        self.queue = []
67
 
        self.lock.release()
68
 
        return s
69
 
 
70
 
class WidgetProxyData(object):
71
 
    pass
72
 
 
73
 
class WidgetProxy(QObject):
74
 
    """Handle Shell widget refresh signal"""
75
 
    def __init__(self, input_condition):
76
 
        QObject.__init__(self)
77
 
        self.input_data = None
78
 
        self.input_condition = input_condition
79
 
        
80
 
    def new_prompt(self, prompt):
81
 
        self.emit(SIGNAL("new_prompt(QString)"), prompt)
82
 
        
83
 
    def set_readonly(self, state):
84
 
        self.emit(SIGNAL("set_readonly(bool)"), state)
85
 
        
86
 
    def edit(self, filename, external_editor=False):
87
 
        self.emit(SIGNAL("edit(QString,bool)"), filename, external_editor)
88
 
    
89
 
    def data_available(self):
90
 
        """Return True if input data is available"""
91
 
        return self.input_data is not WidgetProxyData
92
 
    
93
 
    def wait_input(self, prompt=''):
94
 
        self.input_data = WidgetProxyData
95
 
        self.emit(SIGNAL("wait_input(QString)"), prompt)
96
 
    
97
 
    def end_input(self, cmd):
98
 
        self.input_condition.acquire()
99
 
        self.input_data = cmd
100
 
        self.input_condition.notify()
101
 
        self.input_condition.release()
102
 
 
103
 
 
104
 
class InternalShell(PythonShellWidget):
105
 
    """Shell base widget: link between PythonShellWidget and Interpreter"""
106
 
    def __init__(self, parent=None, namespace=None, commands=[], message="",
107
 
                 max_line_count=300, font=None, debug=False, exitfunc=None,
108
 
                 profile=False, multithreaded=True, light_background=True):
109
 
        PythonShellWidget.__init__(self, parent,
110
 
                                   get_conf_path('.history_internal.py'),
111
 
                                   debug, profile)
112
 
        
113
 
        self.set_light_background(light_background)
114
 
        
115
 
        self.multithreaded = multithreaded
116
 
        
117
 
        self.setMaximumBlockCount(max_line_count)
118
 
        
119
 
        if font is not None:
120
 
            self.set_font(font)
121
 
        
122
 
        # Allow raw_input support:
123
 
        self.input_loop = None
124
 
        self.input_mode = False
125
 
        
126
 
        # KeyboardInterrupt support
127
 
        self.interrupted = False # used only for not-multithreaded mode
128
 
        self.connect(self, SIGNAL("keyboard_interrupt()"),
129
 
                     self.keyboard_interrupt)
130
 
        
131
 
        # Code completion / calltips
132
 
        getcfg = lambda option: CONF.get('internal_console', option)
133
 
        case_sensitive = getcfg('codecompletion/case_sensitive')
134
 
        show_single = getcfg('codecompletion/show_single')
135
 
        self.set_codecompletion_case(case_sensitive)
136
 
        self.set_codecompletion_single(show_single)
137
 
        
138
 
        # keyboard events management
139
 
        self.eventqueue = []
140
 
 
141
 
        # Init interpreter
142
 
        self.exitfunc = exitfunc
143
 
        self.commands = commands
144
 
        self.message = message
145
 
        self.interpreter = None
146
 
        self.start_interpreter(namespace)
147
 
        
148
 
        # Clear status bar
149
 
        self.emit(SIGNAL("status(QString)"), '')
150
 
        
151
 
        # Embedded shell -- requires the monitor (which installs the
152
 
        # 'open_in_spyder' function in builtins)
153
 
        if hasattr(__builtin__, 'open_in_spyder'):
154
 
            self.connect(self, SIGNAL("go_to_error(QString)"),
155
 
                         self.open_with_external_spyder)
156
 
 
157
 
 
158
 
    #------ Interpreter
159
 
    def start_interpreter(self, namespace):
160
 
        """Start Python interpreter"""
161
 
        self.clear()
162
 
        
163
 
        if self.interpreter is not None:
164
 
            self.interpreter.closing()
165
 
        self.interpreter = Interpreter(namespace, self.exitfunc,
166
 
                                       SysOutput, WidgetProxy, self.debug)
167
 
        self.connect(self.interpreter.stdout_write,
168
 
                     SIGNAL("void data_avail()"), self.stdout_avail)
169
 
        self.connect(self.interpreter.stderr_write,
170
 
                     SIGNAL("void data_avail()"), self.stderr_avail)
171
 
        self.connect(self.interpreter.widget_proxy,
172
 
                     SIGNAL("set_readonly(bool)"), self.setReadOnly)
173
 
        self.connect(self.interpreter.widget_proxy,
174
 
                     SIGNAL("new_prompt(QString)"), self.new_prompt)
175
 
        self.connect(self.interpreter.widget_proxy,
176
 
                     SIGNAL("edit(QString,bool)"), self.edit_script)
177
 
        self.connect(self.interpreter.widget_proxy,
178
 
                     SIGNAL("wait_input(QString)"), self.wait_input)
179
 
        if self.multithreaded:
180
 
            self.interpreter.start()
181
 
        
182
 
        # Interpreter banner
183
 
        banner = create_banner(self.message)
184
 
        self.write(banner, prompt=True)
185
 
 
186
 
        # Initial commands
187
 
        for cmd in self.commands:
188
 
            self.run_command(cmd, history=False, new_prompt=False)
189
 
                
190
 
        # First prompt
191
 
        self.new_prompt(self.interpreter.p1)
192
 
        self.emit(SIGNAL("refresh()"))
193
 
 
194
 
        return self.interpreter
195
 
 
196
 
    def exit_interpreter(self):
197
 
        """Exit interpreter"""
198
 
        self.interpreter.exit_flag = True
199
 
        if self.multithreaded:
200
 
            self.interpreter.stdin_write.write('\n')
201
 
        self.interpreter.restore_stds()
202
 
        
203
 
    def edit_script(self, filename, external_editor):
204
 
        filename = unicode(filename)
205
 
        if external_editor:
206
 
            self.external_editor(filename)
207
 
        else:
208
 
            self.parent().edit_script(filename)            
209
 
                                    
210
 
    def stdout_avail(self):
211
 
        """Data is available in stdout, let's empty the queue and write it!"""
212
 
        data = self.interpreter.stdout_write.empty_queue()
213
 
        if data:
214
 
            self.write(data)
215
 
        
216
 
    def stderr_avail(self):
217
 
        """Data is available in stderr, let's empty the queue and write it!"""
218
 
        data = self.interpreter.stderr_write.empty_queue()
219
 
        if data:
220
 
            self.write(data, error=True)
221
 
            self.flush(error=True)
222
 
    
223
 
    
224
 
    #------Raw input support
225
 
    def wait_input(self, prompt=''):
226
 
        """Wait for input (raw_input support)"""
227
 
        self.new_prompt(prompt)
228
 
        self.setFocus()
229
 
        self.input_mode = True
230
 
        self.input_loop = QEventLoop()
231
 
        self.input_loop.exec_()
232
 
        self.input_loop = None
233
 
    
234
 
    def end_input(self, cmd):
235
 
        """End of wait_input mode"""
236
 
        self.input_mode = False
237
 
        self.input_loop.exit()
238
 
        self.interpreter.widget_proxy.end_input(cmd)
239
 
 
240
 
 
241
 
    #----- Menus, actions, ...
242
 
    def setup_context_menu(self):
243
 
        """Reimplement PythonShellWidget method"""
244
 
        PythonShellWidget.setup_context_menu(self)
245
 
        self.help_action = create_action(self, _("Help..."),
246
 
                           icon=get_std_icon('DialogHelpButton'),
247
 
                           triggered=self.help)
248
 
        self.menu.addAction(self.help_action)
249
 
 
250
 
    def help(self):
251
 
        """Help on Spyder console"""
252
 
        QMessageBox.about(self, _("Help"),
253
 
                          """<b>%s</b>
254
 
                          <p><i>%s</i><br>    edit foobar.py
255
 
                          <p><i>%s</i><br>    xedit foobar.py
256
 
                          <p><i>%s</i><br>    run foobar.py
257
 
                          <p><i>%s</i><br>    clear x, y
258
 
                          <p><i>%s</i><br>    !ls
259
 
                          <p><i>%s</i><br>    object?
260
 
                          <p><i>%s</i><br>    result = oedit(object)
261
 
                          """ % (_('Shell special commands:'),
262
 
                                 _('Internal editor:'),
263
 
                                 _('External editor:'),
264
 
                                 _('Run script:'),
265
 
                                 _('Remove references:'),
266
 
                                 _('System commands:'),
267
 
                                 _('Python help:'),
268
 
                                 _('GUI-based editor:')))
269
 
 
270
 
 
271
 
    #------ External editing
272
 
    def open_with_external_spyder(self, text):
273
 
        """Load file in external Spyder's editor, if available
274
 
        This method is used only for embedded consoles
275
 
        (could also be useful if we ever implement the magic %edit command)"""
276
 
        match = get_error_match(unicode(text))
277
 
        if match:
278
 
            fname, lnb = match.groups()
279
 
            __builtin__.open_in_spyder(fname, int(lnb))
280
 
 
281
 
    def external_editor(self, filename, goto=-1):
282
 
        """Edit in an external editor
283
 
        Recommended: SciTE (e.g. to go to line where an error did occur)"""
284
 
        editor_path = CONF.get('internal_console', 'external_editor/path')
285
 
        goto_option = CONF.get('internal_console', 'external_editor/gotoline')
286
 
        try:
287
 
            if goto > 0 and goto_option:
288
 
                Popen(r'%s "%s" %s%d' % (editor_path, filename,
289
 
                                         goto_option, goto))
290
 
            else:
291
 
                Popen(r'%s "%s"' % (editor_path, filename))
292
 
        except OSError:
293
 
            self.write_error("External editor was not found:"
294
 
                             " %s\n" % editor_path)
295
 
 
296
 
 
297
 
    #------ I/O
298
 
    def flush(self, error=False, prompt=False):
299
 
        """Reimplement ShellBaseWidget method"""
300
 
        PythonShellWidget.flush(self, error=error, prompt=prompt)
301
 
        if self.interrupted:
302
 
            self.interrupted = False
303
 
            raise KeyboardInterrupt
304
 
 
305
 
 
306
 
    #------ Clear terminal
307
 
    def clear_terminal(self):
308
 
        """Reimplement ShellBaseWidget method"""
309
 
        self.clear()
310
 
        self.new_prompt(self.interpreter.p2 if self.interpreter.more else self.interpreter.p1)
311
 
 
312
 
 
313
 
    #------ Keyboard events
314
 
    def on_enter(self, command):
315
 
        """on_enter"""
316
 
        if self.profile:
317
 
            # Simple profiling test
318
 
            t0 = time()
319
 
            for _ in range(10):
320
 
                self.execute_command(command)
321
 
            self.insert_text(u"\n<Δt>=%dms\n" % (1e2*(time()-t0)))
322
 
            self.new_prompt(self.interpreter.p1)
323
 
        else:
324
 
            self.execute_command(command)
325
 
        self.__flush_eventqueue()
326
 
 
327
 
    def keyPressEvent(self, event):
328
 
        """
329
 
        Reimplement Qt Method
330
 
        Enhanced keypress event handler
331
 
        """
332
 
        if self.preprocess_keyevent(event):
333
 
            # Event was accepted in self.preprocess_keyevent
334
 
            return
335
 
        self.postprocess_keyevent(event)
336
 
        
337
 
    def __flush_eventqueue(self):
338
 
        """Flush keyboard event queue"""
339
 
        while self.eventqueue:
340
 
            past_event = self.eventqueue.pop(0)
341
 
            self.postprocess_keyevent(past_event)
342
 
        
343
 
    #------ Command execution
344
 
    def keyboard_interrupt(self):
345
 
        """Simulate keyboard interrupt"""
346
 
        if self.multithreaded:
347
 
            self.interpreter.raise_keyboard_interrupt()
348
 
        else:
349
 
            if self.interpreter.more:
350
 
                self.write_error("\nKeyboardInterrupt\n")
351
 
                self.interpreter.more = False
352
 
                self.new_prompt(self.interpreter.p1)
353
 
                self.interpreter.resetbuffer()
354
 
            else:
355
 
                self.interrupted = True
356
 
 
357
 
    def execute_lines(self, lines):
358
 
        """
359
 
        Execute a set of lines as multiple command
360
 
        lines: multiple lines of text to be executed as single commands
361
 
        """
362
 
        for line in lines.splitlines():
363
 
            stripped_line = line.strip()
364
 
            if stripped_line.startswith('#'):
365
 
                continue
366
 
            self.write(line+os.linesep, flush=True)
367
 
            self.execute_command(line+"\n")
368
 
            self.flush()
369
 
        
370
 
    def execute_command(self, cmd):
371
 
        """
372
 
        Execute a command
373
 
        cmd: one-line command only, with '\n' at the end
374
 
        """
375
 
        if self.input_mode:
376
 
            self.end_input(cmd)
377
 
            return
378
 
        if cmd.endswith('\n'):
379
 
            cmd = cmd[:-1]
380
 
        # cls command
381
 
        if cmd == 'cls':
382
 
            self.clear_terminal()
383
 
            return
384
 
        self.run_command(cmd)
385
 
       
386
 
    def run_command(self, cmd, history=True, new_prompt=True):
387
 
        """Run command in interpreter"""
388
 
        if not cmd:
389
 
            cmd = ''
390
 
        else:
391
 
            if history:
392
 
                self.add_to_history(cmd)
393
 
        self.interpreter.stdin_write.write(cmd.encode("utf-8") + '\n')
394
 
        if not self.multithreaded:
395
 
            self.interpreter.run_line()
396
 
            self.emit(SIGNAL("refresh()"))
397
 
    
398
 
    
399
 
    #------ Code completion / Calltips
400
 
    def _eval(self, text):
401
 
        """Is text a valid object?"""
402
 
        return self.interpreter.eval(text)
403
 
                
404
 
    def get_dir(self, objtxt):
405
 
        """Return dir(object)"""
406
 
        obj, valid = self._eval(objtxt)
407
 
        if valid:
408
 
            return getobjdir(obj)
409
 
        
410
 
    def get_globals_keys(self):
411
 
        """Return shell globals() keys"""
412
 
        return self.interpreter.namespace.keys()
413
 
        
414
 
    def get_cdlistdir(self):
415
 
        """Return shell current directory list dir"""
416
 
        return os.listdir(os.getcwdu())
417
 
                
418
 
    def iscallable(self, objtxt):
419
 
        """Is object callable?"""
420
 
        obj, valid = self._eval(objtxt)
421
 
        if valid:
422
 
            return callable(obj)
423
 
    
424
 
    def get_arglist(self, objtxt):
425
 
        """Get func/method argument list"""
426
 
        obj, valid = self._eval(objtxt)
427
 
        if valid:
428
 
            return getargtxt(obj)
429
 
    
430
 
    def get__doc__(self, objtxt):
431
 
        """Get object __doc__"""
432
 
        obj, valid = self._eval(objtxt)
433
 
        if valid:
434
 
            return obj.__doc__
435
 
    
436
 
    def get_doc(self, objtxt):
437
 
        """Get object documentation"""
438
 
        obj, valid = self._eval(objtxt)
439
 
        if valid:
440
 
            return getdoc(obj)
441
 
    
442
 
    def get_source(self, objtxt):
443
 
        """Get object source"""
444
 
        obj, valid = self._eval(objtxt)
445
 
        if valid:
446
 
            return getsource(obj)
447
 
 
448
 
    def is_defined(self, objtxt, force_import=False):
449
 
        """Return True if object is defined"""
450
 
        return self.interpreter.is_defined(objtxt, force_import)
 
1
# -*- coding: utf-8 -*-
 
2
#
 
3
# Copyright © 2009-2010 Pierre Raybaut
 
4
# Licensed under the terms of the MIT License
 
5
# (see spyderlib/__init__.py for details)
 
6
 
 
7
"""Internal shell widget : PythonShellWidget + Interpreter"""
 
8
 
 
9
# pylint: disable=C0103
 
10
# pylint: disable=R0903
 
11
# pylint: disable=R0911
 
12
# pylint: disable=R0201
 
13
 
 
14
#FIXME: Internal shell MT: for i in range(100000): print i -> bug
 
15
 
 
16
#----Builtins
 
17
import __builtin__
 
18
from spyderlib.widgets.objecteditor import oedit
 
19
__builtin__.oedit = oedit
 
20
 
 
21
import os
 
22
import threading
 
23
from time import time
 
24
from subprocess import Popen
 
25
 
 
26
from spyderlib.qt.QtGui import QMessageBox
 
27
from spyderlib.qt.QtCore import SIGNAL, QObject, QEventLoop
 
28
 
 
29
# Local import
 
30
from spyderlib import get_versions
 
31
from spyderlib.utils.qthelpers import create_action, get_std_icon
 
32
from spyderlib.interpreter import Interpreter
 
33
from spyderlib.utils.dochelpers import getargtxt, getsource, getdoc, getobjdir
 
34
from spyderlib.utils.misc import get_error_match
 
35
#TODO: remove the CONF object and make it work anyway
 
36
# In fact, this 'CONF' object has nothing to do in package spyderlib.widgets
 
37
# which should not contain anything directly related to Spyder's main app
 
38
from spyderlib.baseconfig import get_conf_path, _
 
39
from spyderlib.config import CONF
 
40
from spyderlib.widgets.shell import PythonShellWidget
 
41
 
 
42
 
 
43
def create_banner(message):
 
44
    """Create internal shell banner"""
 
45
    versions = get_versions()
 
46
    return 'Spyder %s internal shell on Python %s %dbits [%s]\n%s'\
 
47
           % (versions['spyder'], versions['python'], versions['bitness'],
 
48
              versions['system'], message)
 
49
 
 
50
 
 
51
class SysOutput(QObject):
 
52
    """Handle standard I/O queue"""
 
53
    def __init__(self):
 
54
        QObject.__init__(self)
 
55
        self.queue = []
 
56
        self.lock = threading.Lock()
 
57
        
 
58
    def write(self, val):
 
59
        self.lock.acquire()
 
60
        self.queue.append(val)
 
61
        self.lock.release()
 
62
        self.emit(SIGNAL("void data_avail()"))
 
63
 
 
64
    def empty_queue(self):
 
65
        self.lock.acquire()
 
66
        s = "".join(self.queue)
 
67
        self.queue = []
 
68
        self.lock.release()
 
69
        return s
 
70
 
 
71
class WidgetProxyData(object):
 
72
    pass
 
73
 
 
74
class WidgetProxy(QObject):
 
75
    """Handle Shell widget refresh signal"""
 
76
    def __init__(self, input_condition):
 
77
        QObject.__init__(self)
 
78
        self.input_data = None
 
79
        self.input_condition = input_condition
 
80
        
 
81
    def new_prompt(self, prompt):
 
82
        self.emit(SIGNAL("new_prompt(QString)"), prompt)
 
83
        
 
84
    def set_readonly(self, state):
 
85
        self.emit(SIGNAL("set_readonly(bool)"), state)
 
86
        
 
87
    def edit(self, filename, external_editor=False):
 
88
        self.emit(SIGNAL("edit(QString,bool)"), filename, external_editor)
 
89
    
 
90
    def data_available(self):
 
91
        """Return True if input data is available"""
 
92
        return self.input_data is not WidgetProxyData
 
93
    
 
94
    def wait_input(self, prompt=''):
 
95
        self.input_data = WidgetProxyData
 
96
        self.emit(SIGNAL("wait_input(QString)"), prompt)
 
97
    
 
98
    def end_input(self, cmd):
 
99
        self.input_condition.acquire()
 
100
        self.input_data = cmd
 
101
        self.input_condition.notify()
 
102
        self.input_condition.release()
 
103
 
 
104
 
 
105
class InternalShell(PythonShellWidget):
 
106
    """Shell base widget: link between PythonShellWidget and Interpreter"""
 
107
    def __init__(self, parent=None, namespace=None, commands=[], message="",
 
108
                 max_line_count=300, font=None, debug=False, exitfunc=None,
 
109
                 profile=False, multithreaded=True, light_background=True):
 
110
        PythonShellWidget.__init__(self, parent,
 
111
                                   get_conf_path('.history_internal.py'),
 
112
                                   debug, profile)
 
113
        
 
114
        self.set_light_background(light_background)
 
115
        
 
116
        self.multithreaded = multithreaded
 
117
        
 
118
        self.setMaximumBlockCount(max_line_count)
 
119
        
 
120
        if font is not None:
 
121
            self.set_font(font)
 
122
        
 
123
        # Allow raw_input support:
 
124
        self.input_loop = None
 
125
        self.input_mode = False
 
126
        
 
127
        # KeyboardInterrupt support
 
128
        self.interrupted = False # used only for not-multithreaded mode
 
129
        self.connect(self, SIGNAL("keyboard_interrupt()"),
 
130
                     self.keyboard_interrupt)
 
131
        
 
132
        # Code completion / calltips
 
133
        getcfg = lambda option: CONF.get('internal_console', option)
 
134
        case_sensitive = getcfg('codecompletion/case_sensitive')
 
135
        show_single = getcfg('codecompletion/show_single')
 
136
        self.set_codecompletion_case(case_sensitive)
 
137
        self.set_codecompletion_single(show_single)
 
138
        
 
139
        # keyboard events management
 
140
        self.eventqueue = []
 
141
 
 
142
        # Init interpreter
 
143
        self.exitfunc = exitfunc
 
144
        self.commands = commands
 
145
        self.message = message
 
146
        self.interpreter = None
 
147
        self.start_interpreter(namespace)
 
148
        
 
149
        # Clear status bar
 
150
        self.emit(SIGNAL("status(QString)"), '')
 
151
        
 
152
        # Embedded shell -- requires the monitor (which installs the
 
153
        # 'open_in_spyder' function in builtins)
 
154
        if hasattr(__builtin__, 'open_in_spyder'):
 
155
            self.connect(self, SIGNAL("go_to_error(QString)"),
 
156
                         self.open_with_external_spyder)
 
157
 
 
158
 
 
159
    #------ Interpreter
 
160
    def start_interpreter(self, namespace):
 
161
        """Start Python interpreter"""
 
162
        self.clear()
 
163
        
 
164
        if self.interpreter is not None:
 
165
            self.interpreter.closing()
 
166
        self.interpreter = Interpreter(namespace, self.exitfunc,
 
167
                                       SysOutput, WidgetProxy, self.debug)
 
168
        self.connect(self.interpreter.stdout_write,
 
169
                     SIGNAL("void data_avail()"), self.stdout_avail)
 
170
        self.connect(self.interpreter.stderr_write,
 
171
                     SIGNAL("void data_avail()"), self.stderr_avail)
 
172
        self.connect(self.interpreter.widget_proxy,
 
173
                     SIGNAL("set_readonly(bool)"), self.setReadOnly)
 
174
        self.connect(self.interpreter.widget_proxy,
 
175
                     SIGNAL("new_prompt(QString)"), self.new_prompt)
 
176
        self.connect(self.interpreter.widget_proxy,
 
177
                     SIGNAL("edit(QString,bool)"), self.edit_script)
 
178
        self.connect(self.interpreter.widget_proxy,
 
179
                     SIGNAL("wait_input(QString)"), self.wait_input)
 
180
        if self.multithreaded:
 
181
            self.interpreter.start()
 
182
        
 
183
        # Interpreter banner
 
184
        banner = create_banner(self.message)
 
185
        self.write(banner, prompt=True)
 
186
 
 
187
        # Initial commands
 
188
        for cmd in self.commands:
 
189
            self.run_command(cmd, history=False, new_prompt=False)
 
190
                
 
191
        # First prompt
 
192
        self.new_prompt(self.interpreter.p1)
 
193
        self.emit(SIGNAL("refresh()"))
 
194
 
 
195
        return self.interpreter
 
196
 
 
197
    def exit_interpreter(self):
 
198
        """Exit interpreter"""
 
199
        self.interpreter.exit_flag = True
 
200
        if self.multithreaded:
 
201
            self.interpreter.stdin_write.write('\n')
 
202
        self.interpreter.restore_stds()
 
203
        
 
204
    def edit_script(self, filename, external_editor):
 
205
        filename = unicode(filename)
 
206
        if external_editor:
 
207
            self.external_editor(filename)
 
208
        else:
 
209
            self.parent().edit_script(filename)            
 
210
                                    
 
211
    def stdout_avail(self):
 
212
        """Data is available in stdout, let's empty the queue and write it!"""
 
213
        data = self.interpreter.stdout_write.empty_queue()
 
214
        if data:
 
215
            self.write(data)
 
216
        
 
217
    def stderr_avail(self):
 
218
        """Data is available in stderr, let's empty the queue and write it!"""
 
219
        data = self.interpreter.stderr_write.empty_queue()
 
220
        if data:
 
221
            self.write(data, error=True)
 
222
            self.flush(error=True)
 
223
    
 
224
    
 
225
    #------Raw input support
 
226
    def wait_input(self, prompt=''):
 
227
        """Wait for input (raw_input support)"""
 
228
        self.new_prompt(prompt)
 
229
        self.setFocus()
 
230
        self.input_mode = True
 
231
        self.input_loop = QEventLoop()
 
232
        self.input_loop.exec_()
 
233
        self.input_loop = None
 
234
    
 
235
    def end_input(self, cmd):
 
236
        """End of wait_input mode"""
 
237
        self.input_mode = False
 
238
        self.input_loop.exit()
 
239
        self.interpreter.widget_proxy.end_input(cmd)
 
240
 
 
241
 
 
242
    #----- Menus, actions, ...
 
243
    def setup_context_menu(self):
 
244
        """Reimplement PythonShellWidget method"""
 
245
        PythonShellWidget.setup_context_menu(self)
 
246
        self.help_action = create_action(self, _("Help..."),
 
247
                           icon=get_std_icon('DialogHelpButton'),
 
248
                           triggered=self.help)
 
249
        self.menu.addAction(self.help_action)
 
250
 
 
251
    def help(self):
 
252
        """Help on Spyder console"""
 
253
        QMessageBox.about(self, _("Help"),
 
254
                          """<b>%s</b>
 
255
                          <p><i>%s</i><br>    edit foobar.py
 
256
                          <p><i>%s</i><br>    xedit foobar.py
 
257
                          <p><i>%s</i><br>    run foobar.py
 
258
                          <p><i>%s</i><br>    clear x, y
 
259
                          <p><i>%s</i><br>    !ls
 
260
                          <p><i>%s</i><br>    object?
 
261
                          <p><i>%s</i><br>    result = oedit(object)
 
262
                          """ % (_('Shell special commands:'),
 
263
                                 _('Internal editor:'),
 
264
                                 _('External editor:'),
 
265
                                 _('Run script:'),
 
266
                                 _('Remove references:'),
 
267
                                 _('System commands:'),
 
268
                                 _('Python help:'),
 
269
                                 _('GUI-based editor:')))
 
270
 
 
271
 
 
272
    #------ External editing
 
273
    def open_with_external_spyder(self, text):
 
274
        """Load file in external Spyder's editor, if available
 
275
        This method is used only for embedded consoles
 
276
        (could also be useful if we ever implement the magic %edit command)"""
 
277
        match = get_error_match(unicode(text))
 
278
        if match:
 
279
            fname, lnb = match.groups()
 
280
            __builtin__.open_in_spyder(fname, int(lnb))
 
281
 
 
282
    def external_editor(self, filename, goto=-1):
 
283
        """Edit in an external editor
 
284
        Recommended: SciTE (e.g. to go to line where an error did occur)"""
 
285
        editor_path = CONF.get('internal_console', 'external_editor/path')
 
286
        goto_option = CONF.get('internal_console', 'external_editor/gotoline')
 
287
        try:
 
288
            if goto > 0 and goto_option:
 
289
                Popen(r'%s "%s" %s%d' % (editor_path, filename,
 
290
                                         goto_option, goto))
 
291
            else:
 
292
                Popen(r'%s "%s"' % (editor_path, filename))
 
293
        except OSError:
 
294
            self.write_error("External editor was not found:"
 
295
                             " %s\n" % editor_path)
 
296
 
 
297
 
 
298
    #------ I/O
 
299
    def flush(self, error=False, prompt=False):
 
300
        """Reimplement ShellBaseWidget method"""
 
301
        PythonShellWidget.flush(self, error=error, prompt=prompt)
 
302
        if self.interrupted:
 
303
            self.interrupted = False
 
304
            raise KeyboardInterrupt
 
305
 
 
306
 
 
307
    #------ Clear terminal
 
308
    def clear_terminal(self):
 
309
        """Reimplement ShellBaseWidget method"""
 
310
        self.clear()
 
311
        self.new_prompt(self.interpreter.p2 if self.interpreter.more else self.interpreter.p1)
 
312
 
 
313
 
 
314
    #------ Keyboard events
 
315
    def on_enter(self, command):
 
316
        """on_enter"""
 
317
        if self.profile:
 
318
            # Simple profiling test
 
319
            t0 = time()
 
320
            for _ in range(10):
 
321
                self.execute_command(command)
 
322
            self.insert_text(u"\n<Δt>=%dms\n" % (1e2*(time()-t0)))
 
323
            self.new_prompt(self.interpreter.p1)
 
324
        else:
 
325
            self.execute_command(command)
 
326
        self.__flush_eventqueue()
 
327
 
 
328
    def keyPressEvent(self, event):
 
329
        """
 
330
        Reimplement Qt Method
 
331
        Enhanced keypress event handler
 
332
        """
 
333
        if self.preprocess_keyevent(event):
 
334
            # Event was accepted in self.preprocess_keyevent
 
335
            return
 
336
        self.postprocess_keyevent(event)
 
337
        
 
338
    def __flush_eventqueue(self):
 
339
        """Flush keyboard event queue"""
 
340
        while self.eventqueue:
 
341
            past_event = self.eventqueue.pop(0)
 
342
            self.postprocess_keyevent(past_event)
 
343
        
 
344
    #------ Command execution
 
345
    def keyboard_interrupt(self):
 
346
        """Simulate keyboard interrupt"""
 
347
        if self.multithreaded:
 
348
            self.interpreter.raise_keyboard_interrupt()
 
349
        else:
 
350
            if self.interpreter.more:
 
351
                self.write_error("\nKeyboardInterrupt\n")
 
352
                self.interpreter.more = False
 
353
                self.new_prompt(self.interpreter.p1)
 
354
                self.interpreter.resetbuffer()
 
355
            else:
 
356
                self.interrupted = True
 
357
 
 
358
    def execute_lines(self, lines):
 
359
        """
 
360
        Execute a set of lines as multiple command
 
361
        lines: multiple lines of text to be executed as single commands
 
362
        """
 
363
        for line in lines.splitlines():
 
364
            stripped_line = line.strip()
 
365
            if stripped_line.startswith('#'):
 
366
                continue
 
367
            self.write(line+os.linesep, flush=True)
 
368
            self.execute_command(line+"\n")
 
369
            self.flush()
 
370
        
 
371
    def execute_command(self, cmd):
 
372
        """
 
373
        Execute a command
 
374
        cmd: one-line command only, with '\n' at the end
 
375
        """
 
376
        if self.input_mode:
 
377
            self.end_input(cmd)
 
378
            return
 
379
        if cmd.endswith('\n'):
 
380
            cmd = cmd[:-1]
 
381
        # cls command
 
382
        if cmd == 'cls':
 
383
            self.clear_terminal()
 
384
            return
 
385
        self.run_command(cmd)
 
386
       
 
387
    def run_command(self, cmd, history=True, new_prompt=True):
 
388
        """Run command in interpreter"""
 
389
        if not cmd:
 
390
            cmd = ''
 
391
        else:
 
392
            if history:
 
393
                self.add_to_history(cmd)
 
394
        self.interpreter.stdin_write.write(cmd.encode("utf-8") + '\n')
 
395
        if not self.multithreaded:
 
396
            self.interpreter.run_line()
 
397
            self.emit(SIGNAL("refresh()"))
 
398
    
 
399
    
 
400
    #------ Code completion / Calltips
 
401
    def _eval(self, text):
 
402
        """Is text a valid object?"""
 
403
        return self.interpreter.eval(text)
 
404
                
 
405
    def get_dir(self, objtxt):
 
406
        """Return dir(object)"""
 
407
        obj, valid = self._eval(objtxt)
 
408
        if valid:
 
409
            return getobjdir(obj)
 
410
        
 
411
    def get_globals_keys(self):
 
412
        """Return shell globals() keys"""
 
413
        return self.interpreter.namespace.keys()
 
414
        
 
415
    def get_cdlistdir(self):
 
416
        """Return shell current directory list dir"""
 
417
        return os.listdir(os.getcwdu())
 
418
                
 
419
    def iscallable(self, objtxt):
 
420
        """Is object callable?"""
 
421
        obj, valid = self._eval(objtxt)
 
422
        if valid:
 
423
            return callable(obj)
 
424
    
 
425
    def get_arglist(self, objtxt):
 
426
        """Get func/method argument list"""
 
427
        obj, valid = self._eval(objtxt)
 
428
        if valid:
 
429
            return getargtxt(obj)
 
430
    
 
431
    def get__doc__(self, objtxt):
 
432
        """Get object __doc__"""
 
433
        obj, valid = self._eval(objtxt)
 
434
        if valid:
 
435
            return obj.__doc__
 
436
    
 
437
    def get_doc(self, objtxt):
 
438
        """Get object documentation"""
 
439
        obj, valid = self._eval(objtxt)
 
440
        if valid:
 
441
            return getdoc(obj)
 
442
    
 
443
    def get_source(self, objtxt):
 
444
        """Get object source"""
 
445
        obj, valid = self._eval(objtxt)
 
446
        if valid:
 
447
            return getsource(obj)
 
448
 
 
449
    def is_defined(self, objtxt, force_import=False):
 
450
        """Return True if object is defined"""
 
451
        return self.interpreter.is_defined(objtxt, force_import)