blob: 52b18bb292111cfba17c6e8b49bc1e790fadb0f1 [file] [log] [blame]
Tor Norbye1aa2e092014-08-20 17:01:23 -07001'''An helper file for the pydev debugger (REPL) console
2'''
3from code import InteractiveConsole
4import sys
5import traceback
6
7import _pydev_completer
8from pydevd_tracing import GetExceptionTracebackStr
9from pydevd_vars import makeValidXmlValue
10from pydev_imports import Exec
11from pydevd_io import IOBuf
12from pydev_console_utils import BaseInterpreterInterface, BaseStdIn
13from pydev_override import overrides
14import pydevd_save_locals
15
16CONSOLE_OUTPUT = "output"
17CONSOLE_ERROR = "error"
18
19
20#=======================================================================================================================
21# ConsoleMessage
22#=======================================================================================================================
23class ConsoleMessage:
24 """Console Messages
25 """
26 def __init__(self):
27 self.more = False
28 # List of tuple [('error', 'error_message'), ('message_list', 'output_message')]
29 self.console_messages = []
30
31 def add_console_message(self, message_type, message):
32 """add messages in the console_messages list
33 """
34 for m in message.split("\n"):
35 if m.strip():
36 self.console_messages.append((message_type, m))
37
38 def update_more(self, more):
39 """more is set to true if further input is required from the user
40 else more is set to false
41 """
42 self.more = more
43
44 def toXML(self):
45 """Create an XML for console message_list, error and more (true/false)
46 <xml>
47 <message_list>console message_list</message_list>
48 <error>console error</error>
49 <more>true/false</more>
50 </xml>
51 """
52 makeValid = makeValidXmlValue
53
54 xml = '<xml><more>%s</more>' % (self.more)
55
56 for message_type, message in self.console_messages:
57 xml += '<%s message="%s"></%s>' % (message_type, makeValid(message), message_type)
58
59 xml += '</xml>'
60
61 return xml
62
63
64#=======================================================================================================================
65# DebugConsoleStdIn
66#=======================================================================================================================
67class DebugConsoleStdIn(BaseStdIn):
68
69 overrides(BaseStdIn.readline)
70 def readline(self, *args, **kwargs):
71 sys.stderr.write('Warning: Reading from stdin is still not supported in this console.\n')
72 return '\n'
73
74#=======================================================================================================================
75# DebugConsole
76#=======================================================================================================================
77class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
78 """Wrapper around code.InteractiveConsole, in order to send
79 errors and outputs to the debug console
80 """
81
82 overrides(BaseInterpreterInterface.createStdIn)
83 def createStdIn(self):
84 return DebugConsoleStdIn() #For now, raw_input is not supported in this console.
85
86
87 overrides(InteractiveConsole.push)
88 def push(self, line, frame):
89 """Change built-in stdout and stderr methods by the
90 new custom StdMessage.
91 execute the InteractiveConsole.push.
92 Change the stdout and stderr back be the original built-ins
93
94 Return boolean (True if more input is required else False),
95 output_messages and input_messages
96 """
97 more = False
98 original_stdout = sys.stdout
99 original_stderr = sys.stderr
100 try:
101 try:
102 self.frame = frame
103 out = sys.stdout = IOBuf()
104 err = sys.stderr = IOBuf()
105 more = self.addExec(line)
106 except Exception:
107 exc = GetExceptionTracebackStr()
108 err.buflist.append("Internal Error: %s" % (exc,))
109 finally:
110 #Remove frame references.
111 self.frame = None
112 frame = None
113 sys.stdout = original_stdout
114 sys.stderr = original_stderr
115
116 return more, out.buflist, err.buflist
117
118
119 overrides(BaseInterpreterInterface.doAddExec)
120 def doAddExec(self, line):
121 return InteractiveConsole.push(self, line)
122
123
124 overrides(InteractiveConsole.runcode)
125 def runcode(self, code):
126 """Execute a code object.
127
128 When an exception occurs, self.showtraceback() is called to
129 display a traceback. All exceptions are caught except
130 SystemExit, which is reraised.
131
132 A note about KeyboardInterrupt: this exception may occur
133 elsewhere in this code, and may not always be caught. The
134 caller should be prepared to deal with it.
135
136 """
137 try:
138 Exec(code, self.frame.f_globals, self.frame.f_locals)
139 pydevd_save_locals.save_locals(self.frame)
140 except SystemExit:
141 raise
142 except:
143 self.showtraceback()
144
145
146#=======================================================================================================================
147# InteractiveConsoleCache
148#=======================================================================================================================
149class InteractiveConsoleCache:
150
151 thread_id = None
152 frame_id = None
153 interactive_console_instance = None
154
155
156#Note: On Jython 2.1 we can't use classmethod or staticmethod, so, just make the functions below free-functions.
157def get_interactive_console(thread_id, frame_id, frame, console_message):
158 """returns the global interactive console.
159 interactive console should have been initialized by this time
160 """
161 if InteractiveConsoleCache.thread_id == thread_id and InteractiveConsoleCache.frame_id == frame_id:
162 return InteractiveConsoleCache.interactive_console_instance
163
164 InteractiveConsoleCache.interactive_console_instance = DebugConsole()
165 InteractiveConsoleCache.thread_id = thread_id
166 InteractiveConsoleCache.frame_id = frame_id
167
168 console_stacktrace = traceback.extract_stack(frame, limit=1)
169 if console_stacktrace:
170 current_context = console_stacktrace[0] # top entry from stacktrace
171 context_message = 'File "%s", line %s, in %s' % (current_context[0], current_context[1], current_context[2])
172 console_message.add_console_message(CONSOLE_OUTPUT, "[Current context]: %s" % (context_message,))
173 return InteractiveConsoleCache.interactive_console_instance
174
175
176def clear_interactive_console():
177 InteractiveConsoleCache.thread_id = None
178 InteractiveConsoleCache.frame_id = None
179 InteractiveConsoleCache.interactive_console_instance = None
180
181
182def execute_console_command(frame, thread_id, frame_id, line):
183 """fetch an interactive console instance from the cache and
184 push the received command to the console.
185
186 create and return an instance of console_message
187 """
188 console_message = ConsoleMessage()
189
190 interpreter = get_interactive_console(thread_id, frame_id, frame, console_message)
191 more, output_messages, error_messages = interpreter.push(line, frame)
192 console_message.update_more(more)
193
194 for message in output_messages:
195 console_message.add_console_message(CONSOLE_OUTPUT, message)
196
197 for message in error_messages:
198 console_message.add_console_message(CONSOLE_ERROR, message)
199
200 return console_message
201
202
203def get_completions(frame, act_tok):
204 """ fetch all completions, create xml for the same
205 return the completions xml
206 """
207 return _pydev_completer.GenerateCompletionsAsXML(frame, act_tok)
208
209
210
211
212