55 if DebugClientInstance is None or not DebugClientInstance.redirect: |
55 if DebugClientInstance is None or not DebugClientInstance.redirect: |
56 return DebugClientOrigInput(prompt) |
56 return DebugClientOrigInput(prompt) |
57 else: |
57 else: |
58 return DebugClientInstance.input(prompt, echo=echo) |
58 return DebugClientInstance.input(prompt, echo=echo) |
59 |
59 |
|
60 |
60 # Use our own input(). |
61 # Use our own input(). |
61 try: |
62 try: |
62 DebugClientOrigInput = __builtins__.__dict__['input'] |
63 DebugClientOrigInput = __builtins__.__dict__["input"] |
63 __builtins__.__dict__['input'] = DebugClientInput |
64 __builtins__.__dict__["input"] = DebugClientInput |
64 except (AttributeError, KeyError): |
65 except (AttributeError, KeyError): |
65 import __main__ |
66 import __main__ |
66 DebugClientOrigInput = __main__.__builtins__.__dict__['input'] |
67 |
67 __main__.__builtins__.__dict__['input'] = DebugClientInput |
68 DebugClientOrigInput = __main__.__builtins__.__dict__["input"] |
|
69 __main__.__builtins__.__dict__["input"] = DebugClientInput |
68 |
70 |
69 ############################################################################### |
71 ############################################################################### |
70 |
72 |
71 |
73 |
72 def DebugClientClose(fd): |
74 def DebugClientClose(fd): |
73 """ |
75 """ |
74 Replacement for the standard os.close(fd). |
76 Replacement for the standard os.close(fd). |
75 |
77 |
76 @param fd open file descriptor to be closed (integer) |
78 @param fd open file descriptor to be closed (integer) |
77 """ |
79 """ |
78 if DebugClientInstance is None: |
80 if DebugClientInstance is None: |
79 DebugClientOrigClose(fd) |
81 DebugClientOrigClose(fd) |
80 else: |
82 else: |
81 DebugClientInstance.close(fd) |
83 DebugClientInstance.close(fd) |
82 |
84 |
|
85 |
83 # use our own close(). |
86 # use our own close(). |
84 if 'close' in dir(os): |
87 if "close" in dir(os): |
85 DebugClientOrigClose = os.close |
88 DebugClientOrigClose = os.close |
86 os.close = DebugClientClose |
89 os.close = DebugClientClose |
87 |
90 |
88 ############################################################################### |
91 ############################################################################### |
89 |
92 |
90 |
93 |
91 def DebugClientSetRecursionLimit(limit): |
94 def DebugClientSetRecursionLimit(limit): |
92 """ |
95 """ |
93 Replacement for the standard sys.setrecursionlimit(limit). |
96 Replacement for the standard sys.setrecursionlimit(limit). |
94 |
97 |
95 @param limit recursion limit (integer) |
98 @param limit recursion limit (integer) |
96 """ |
99 """ |
97 rl = max(limit, 64) |
100 rl = max(limit, 64) |
98 setRecursionLimit(rl) |
101 setRecursionLimit(rl) |
99 DebugClientOrigSetRecursionLimit(rl + 64) |
102 DebugClientOrigSetRecursionLimit(rl + 64) |
100 |
103 |
|
104 |
101 # use our own setrecursionlimit(). |
105 # use our own setrecursionlimit(). |
102 if 'setrecursionlimit' in dir(sys): |
106 if "setrecursionlimit" in dir(sys): |
103 DebugClientOrigSetRecursionLimit = sys.setrecursionlimit |
107 DebugClientOrigSetRecursionLimit = sys.setrecursionlimit |
104 sys.setrecursionlimit = DebugClientSetRecursionLimit |
108 sys.setrecursionlimit = DebugClientSetRecursionLimit |
105 DebugClientSetRecursionLimit(sys.getrecursionlimit()) |
109 DebugClientSetRecursionLimit(sys.getrecursionlimit()) |
106 |
110 |
107 ############################################################################### |
111 ############################################################################### |
111 """ |
115 """ |
112 Class implementing the client side of the debugger. |
116 Class implementing the client side of the debugger. |
113 |
117 |
114 It provides access to the Python interpeter from a debugger running in |
118 It provides access to the Python interpeter from a debugger running in |
115 another process. |
119 another process. |
116 |
120 |
117 The protocol between the debugger and the client is based on JSONRPC 2.0 |
121 The protocol between the debugger and the client is based on JSONRPC 2.0 |
118 PDUs. Each one is sent on a single line, i.e. commands or responses are |
122 PDUs. Each one is sent on a single line, i.e. commands or responses are |
119 separated by a linefeed character. |
123 separated by a linefeed character. |
120 |
124 |
121 If the debugger closes the session there is no response from the client. |
125 If the debugger closes the session there is no response from the client. |
122 The client may close the session at any time as a result of the script |
126 The client may close the session at any time as a result of the script |
123 being debugged closing or crashing. |
127 being debugged closing or crashing. |
124 |
128 |
125 <b>Note</b>: This class is meant to be subclassed by individual |
129 <b>Note</b>: This class is meant to be subclassed by individual |
126 DebugClient classes. Do not instantiate it directly. |
130 DebugClient classes. Do not instantiate it directly. |
127 """ |
131 """ |
|
132 |
128 clientCapabilities = DebugClientCapabilities.HasAll |
133 clientCapabilities = DebugClientCapabilities.HasAll |
129 |
134 |
130 Type2Indicators = { |
135 Type2Indicators = { |
131 # Python types |
136 # Python types |
132 'list': '[]', |
137 "list": "[]", |
133 'tuple': '()', |
138 "tuple": "()", |
134 'dict': '{:}', # __IGNORE_WARNING_M613__ |
139 "dict": "{:}", # __IGNORE_WARNING_M613__ |
135 'set': '{}', # __IGNORE_WARNING_M613__ |
140 "set": "{}", # __IGNORE_WARNING_M613__ |
136 'frozenset': '{}', # __IGNORE_WARNING_M613__ |
141 "frozenset": "{}", # __IGNORE_WARNING_M613__ |
137 'numpy.ndarray': '[ndarray]', # __IGNORE_WARNING_M613__ |
142 "numpy.ndarray": "[ndarray]", # __IGNORE_WARNING_M613__ |
138 'collections.abc.ItemsView': '[]', |
143 "collections.abc.ItemsView": "[]", |
139 'collections.abc.KeysView': '[]', |
144 "collections.abc.KeysView": "[]", |
140 'collections.abc.ValuesView': '[]', |
145 "collections.abc.ValuesView": "[]", |
141 } |
146 } |
142 |
147 |
143 def __init__(self): |
148 def __init__(self): |
144 """ |
149 """ |
145 Constructor |
150 Constructor |
146 """ |
151 """ |
147 self.breakpoints = {} |
152 self.breakpoints = {} |
148 self.redirect = True |
153 self.redirect = True |
149 |
154 |
150 # special objects representing the main scripts thread and frame |
155 # special objects representing the main scripts thread and frame |
151 self.mainThread = self |
156 self.mainThread = self |
152 self.framenr = 0 |
157 self.framenr = 0 |
153 |
158 |
154 # The context to run the debugged program in. |
159 # The context to run the debugged program in. |
155 self.debugMod = types.ModuleType('__main__') |
160 self.debugMod = types.ModuleType("__main__") |
156 self.debugMod.__dict__['__builtins__'] = __builtins__ |
161 self.debugMod.__dict__["__builtins__"] = __builtins__ |
157 |
162 |
158 # The list of complete lines to execute. |
163 # The list of complete lines to execute. |
159 self.buffer = '' |
164 self.buffer = "" |
160 |
165 |
161 # The precompiled regexp to filter variables against |
166 # The precompiled regexp to filter variables against |
162 self.globalsFilterObjects = None |
167 self.globalsFilterObjects = None |
163 self.localsFilterObjects = None |
168 self.localsFilterObjects = None |
164 |
169 |
165 self._fncache = {} |
170 self._fncache = {} |
166 self.dircache = [] |
171 self.dircache = [] |
167 self.passive = False # used to indicate the passive mode |
172 self.passive = False # used to indicate the passive mode |
168 self.running = None |
173 self.running = None |
169 self.test = None |
174 self.test = None |
170 self.debugging = False |
175 self.debugging = False |
171 self.multiprocessSupport = False |
176 self.multiprocessSupport = False |
172 self.noDebugList = [] |
177 self.noDebugList = [] |
173 |
178 |
174 self.readstream = None |
179 self.readstream = None |
175 self.writestream = None |
180 self.writestream = None |
176 self.errorstream = None |
181 self.errorstream = None |
177 self.pollingDisabled = False |
182 self.pollingDisabled = False |
178 |
183 |
179 self.__debuggerId = "" |
184 self.__debuggerId = "" |
180 |
185 |
181 self.callTraceEnabled = None |
186 self.callTraceEnabled = None |
182 |
187 |
183 self.compile_command = codeop.CommandCompiler() |
188 self.compile_command = codeop.CommandCompiler() |
184 |
189 |
185 self.coding_re = re.compile(r"coding[:=]\s*([-\w_.]+)") |
190 self.coding_re = re.compile(r"coding[:=]\s*([-\w_.]+)") |
186 self.defaultCoding = 'utf-8' |
191 self.defaultCoding = "utf-8" |
187 self.__coding = self.defaultCoding |
192 self.__coding = self.defaultCoding |
188 self.noencoding = False |
193 self.noencoding = False |
189 |
194 |
190 self.startOptions = None |
195 self.startOptions = None |
191 |
196 |
192 def getCoding(self): |
197 def getCoding(self): |
193 """ |
198 """ |
194 Public method to return the current coding. |
199 Public method to return the current coding. |
195 |
200 |
196 @return codec name (string) |
201 @return codec name (string) |
197 """ |
202 """ |
198 return self.__coding |
203 return self.__coding |
199 |
204 |
200 def __setCoding(self, filename): |
205 def __setCoding(self, filename): |
201 """ |
206 """ |
202 Private method to set the coding used by a python file. |
207 Private method to set the coding used by a python file. |
203 |
208 |
204 @param filename name of the file to inspect (string) |
209 @param filename name of the file to inspect (string) |
205 """ |
210 """ |
206 if self.noencoding: |
211 if self.noencoding: |
207 self.__coding = sys.getdefaultencoding() |
212 self.__coding = sys.getdefaultencoding() |
208 else: |
213 else: |
209 default = 'utf-8' |
214 default = "utf-8" |
210 try: |
215 try: |
211 with open(filename, 'rb') as f: |
216 with open(filename, "rb") as f: |
212 # read the first and second line |
217 # read the first and second line |
213 text = f.readline() |
218 text = f.readline() |
214 text = "{0}{1}".format(text, f.readline()) |
219 text = "{0}{1}".format(text, f.readline()) |
215 except OSError: |
220 except OSError: |
216 self.__coding = default |
221 self.__coding = default |
217 return |
222 return |
218 |
223 |
219 for line in text.splitlines(): |
224 for line in text.splitlines(): |
220 m = self.coding_re.search(line) |
225 m = self.coding_re.search(line) |
221 if m: |
226 if m: |
222 self.__coding = m.group(1) |
227 self.__coding = m.group(1) |
223 return |
228 return |
224 self.__coding = default |
229 self.__coding = default |
225 |
230 |
226 def input(self, prompt, echo=True): |
231 def input(self, prompt, echo=True): |
227 """ |
232 """ |
228 Public method to implement input() using the event loop. |
233 Public method to implement input() using the event loop. |
229 |
234 |
230 @param prompt prompt to be shown |
235 @param prompt prompt to be shown |
231 @type str |
236 @type str |
232 @param echo flag indicating echoing of the input |
237 @param echo flag indicating echoing of the input |
233 @type bool |
238 @type bool |
234 @return the entered string |
239 @return the entered string |
235 @rtype str |
240 @rtype str |
236 """ |
241 """ |
237 self.sendJsonCommand("RequestRaw", { |
242 self.sendJsonCommand( |
238 "prompt": prompt, |
243 "RequestRaw", |
239 "echo": echo, |
244 { |
240 }) |
245 "prompt": prompt, |
|
246 "echo": echo, |
|
247 }, |
|
248 ) |
241 self.eventLoop(True) |
249 self.eventLoop(True) |
242 return self.rawLine |
250 return self.rawLine |
243 |
251 |
244 def sessionClose(self, terminate=True): |
252 def sessionClose(self, terminate=True): |
245 """ |
253 """ |
246 Public method to close the session with the debugger and optionally |
254 Public method to close the session with the debugger and optionally |
247 terminate. |
255 terminate. |
248 |
256 |
249 @param terminate flag indicating to terminate (boolean) |
257 @param terminate flag indicating to terminate (boolean) |
250 """ |
258 """ |
251 with contextlib.suppress(Exception): |
259 with contextlib.suppress(Exception): |
252 self.set_quit() |
260 self.set_quit() |
253 |
261 |
254 self.debugging = False |
262 self.debugging = False |
255 self.multiprocessSupport = False |
263 self.multiprocessSupport = False |
256 self.noDebugList = [] |
264 self.noDebugList = [] |
257 |
265 |
258 # make sure we close down our end of the socket |
266 # make sure we close down our end of the socket |
259 # might be overkill as normally stdin, stdout and stderr |
267 # might be overkill as normally stdin, stdout and stderr |
260 # SHOULD be closed on exit, but it does not hurt to do it here |
268 # SHOULD be closed on exit, but it does not hurt to do it here |
261 self.readstream.close(True) |
269 self.readstream.close(True) |
262 self.writestream.close(True) |
270 self.writestream.close(True) |
264 |
272 |
265 if terminate: |
273 if terminate: |
266 # Ok, go away. |
274 # Ok, go away. |
267 sys.exit() |
275 sys.exit() |
268 |
276 |
269 def __compileFileSource(self, filename, mode='exec'): |
277 def __compileFileSource(self, filename, mode="exec"): |
270 """ |
278 """ |
271 Private method to compile source code read from a file. |
279 Private method to compile source code read from a file. |
272 |
280 |
273 @param filename name of the source file |
281 @param filename name of the source file |
274 @type str |
282 @type str |
275 @param mode kind of code to be generated (exec or eval) |
283 @param mode kind of code to be generated (exec or eval) |
276 @type str |
284 @type str |
277 @return compiled code object (None in case of errors) |
285 @return compiled code object (None in case of errors) |
278 """ |
286 """ |
279 with codecs.open(filename, encoding=self.__coding) as fp: |
287 with codecs.open(filename, encoding=self.__coding) as fp: |
280 statement = fp.read() |
288 statement = fp.read() |
281 |
289 |
282 return self.__compileCommand(statement, filename=filename, mode=mode) |
290 return self.__compileCommand(statement, filename=filename, mode=mode) |
283 |
291 |
284 def __compileCommand(self, statement, filename="<string>", mode="exec"): |
292 def __compileCommand(self, statement, filename="<string>", mode="exec"): |
285 """ |
293 """ |
286 Private method to compile source code. |
294 Private method to compile source code. |
287 |
295 |
288 @param statement source code string to be compiled |
296 @param statement source code string to be compiled |
289 @type str |
297 @type str |
290 @param filename name of the source file |
298 @param filename name of the source file |
291 @type str |
299 @type str |
292 @param mode kind of code to be generated (exec or eval) |
300 @param mode kind of code to be generated (exec or eval) |
293 @type str |
301 @type str |
294 @return compiled code object (None in case of errors) |
302 @return compiled code object (None in case of errors) |
295 """ |
303 """ |
296 try: |
304 try: |
297 code = compile(statement + '\n', filename, mode) |
305 code = compile(statement + "\n", filename, mode) |
298 except SyntaxError: |
306 except SyntaxError: |
299 exctype, excval, exctb = sys.exc_info() |
307 exctype, excval, exctb = sys.exc_info() |
300 try: |
308 try: |
301 message = str(excval) |
309 message = str(excval) |
302 filename = excval.filename |
310 filename = excval.filename |
303 lineno = excval.lineno |
311 lineno = excval.lineno |
304 charno = excval.offset |
312 charno = excval.offset |
305 if charno is None: |
313 if charno is None: |
306 charno = 0 |
314 charno = 0 |
307 |
315 |
308 except (AttributeError, ValueError): |
316 except (AttributeError, ValueError): |
309 message = "" |
317 message = "" |
310 filename = "" |
318 filename = "" |
311 lineno = 0 |
319 lineno = 0 |
312 charno = 0 |
320 charno = 0 |
313 |
321 |
314 self.sendSyntaxError(message, filename, lineno, charno, self.name) |
322 self.sendSyntaxError(message, filename, lineno, charno, self.name) |
315 return None |
323 return None |
316 |
324 |
317 return code |
325 return code |
318 |
326 |
319 def handleJsonCommand(self, jsonStr): |
327 def handleJsonCommand(self, jsonStr): |
320 """ |
328 """ |
321 Public method to handle a command serialized as a JSON string. |
329 Public method to handle a command serialized as a JSON string. |
322 |
330 |
323 @param jsonStr string containing the command received from the IDE |
331 @param jsonStr string containing the command received from the IDE |
324 @type str |
332 @type str |
325 """ |
333 """ |
326 ## printerr(jsonStr) ## debug # __IGNORE_WARNING_M891__ |
334 ## printerr(jsonStr) ## debug # __IGNORE_WARNING_M891__ |
327 |
335 |
328 try: |
336 try: |
329 commandDict = json.loads(jsonStr.strip()) |
337 commandDict = json.loads(jsonStr.strip()) |
330 except (TypeError, ValueError) as err: |
338 except (TypeError, ValueError) as err: |
331 printerr("Error handling command: " + jsonStr) |
339 printerr("Error handling command: " + jsonStr) |
332 printerr(str(err)) |
340 printerr(str(err)) |
333 return |
341 return |
334 |
342 |
335 method = commandDict["method"] |
343 method = commandDict["method"] |
336 params = commandDict["params"] |
344 params = commandDict["params"] |
337 |
345 |
338 if method == "RequestVariables": |
346 if method == "RequestVariables": |
339 self.__dumpVariables( |
347 self.__dumpVariables( |
340 params["frameNumber"], params["scope"], params["filters"]) |
348 params["frameNumber"], params["scope"], params["filters"] |
341 |
349 ) |
|
350 |
342 elif method == "RequestVariable": |
351 elif method == "RequestVariable": |
343 self.__dumpVariable( |
352 self.__dumpVariable( |
344 params["variable"], params["frameNumber"], |
353 params["variable"], |
345 params["scope"], params["filters"]) |
354 params["frameNumber"], |
346 |
355 params["scope"], |
|
356 params["filters"], |
|
357 ) |
|
358 |
347 elif method == "RequestStack": |
359 elif method == "RequestStack": |
348 stack = self.mainThread.getStack() |
360 stack = self.mainThread.getStack() |
349 self.sendResponseLine(stack, self.mainThread.name) |
361 self.sendResponseLine(stack, self.mainThread.name) |
350 |
362 |
351 elif method == "RequestThreadList": |
363 elif method == "RequestThreadList": |
352 self.dumpThreadList() |
364 self.dumpThreadList() |
353 |
365 |
354 elif method == "RequestThreadSet": |
366 elif method == "RequestThreadSet": |
355 if params["threadID"] == -1: |
367 if params["threadID"] == -1: |
356 # -1 is indication for the main thread |
368 # -1 is indication for the main thread |
357 threadId = -1 |
369 threadId = -1 |
358 for thread in self.threads.values(): |
370 for thread in self.threads.values(): |
362 threadId = params["threadID"] |
374 threadId = params["threadID"] |
363 if threadId in self.threads: |
375 if threadId in self.threads: |
364 self.setCurrentThread(threadId) |
376 self.setCurrentThread(threadId) |
365 self.sendJsonCommand("ResponseThreadSet", {}) |
377 self.sendJsonCommand("ResponseThreadSet", {}) |
366 stack = self.currentThread.getStack() |
378 stack = self.currentThread.getStack() |
367 self.sendJsonCommand("ResponseStack", { |
379 self.sendJsonCommand( |
368 "stack": stack, |
380 "ResponseStack", |
369 "threadName": self.currentThread.name, |
381 { |
370 }) |
382 "stack": stack, |
371 |
383 "threadName": self.currentThread.name, |
|
384 }, |
|
385 ) |
|
386 |
372 elif method == "RequestDisassembly": |
387 elif method == "RequestDisassembly": |
373 if self.disassembly is not None: |
388 if self.disassembly is not None: |
374 self.sendJsonCommand("ResponseDisassembly", { |
389 self.sendJsonCommand( |
375 "disassembly": self.disassembly |
390 "ResponseDisassembly", {"disassembly": self.disassembly} |
376 }) |
391 ) |
377 else: |
392 else: |
378 self.sendJsonCommand("ResponseDisassembly", { |
393 self.sendJsonCommand("ResponseDisassembly", {"disassembly": {}}) |
379 "disassembly": {} |
394 |
380 }) |
|
381 |
|
382 elif method == "RequestCapabilities": |
395 elif method == "RequestCapabilities": |
383 clientType = "Python3" |
396 clientType = "Python3" |
384 self.sendJsonCommand("ResponseCapabilities", { |
397 self.sendJsonCommand( |
385 "capabilities": self.__clientCapabilities(), |
398 "ResponseCapabilities", |
386 "clientType": clientType |
399 {"capabilities": self.__clientCapabilities(), "clientType": clientType}, |
387 }) |
400 ) |
388 |
401 |
389 elif method == "RequestBanner": |
402 elif method == "RequestBanner": |
390 self.sendJsonCommand("ResponseBanner", { |
403 self.sendJsonCommand( |
391 "version": "Python {0}".format(sys.version), |
404 "ResponseBanner", |
392 "platform": socket.gethostname(), |
405 { |
393 }) |
406 "version": "Python {0}".format(sys.version), |
394 |
407 "platform": socket.gethostname(), |
|
408 }, |
|
409 ) |
|
410 |
395 elif method == "RequestSetFilter": |
411 elif method == "RequestSetFilter": |
396 self.__generateFilterObjects(params["scope"], params["filter"]) |
412 self.__generateFilterObjects(params["scope"], params["filter"]) |
397 |
413 |
398 elif method == "RequestCallTrace": |
414 elif method == "RequestCallTrace": |
399 if params["enable"]: |
415 if params["enable"]: |
400 callTraceEnabled = self.profile |
416 callTraceEnabled = self.profile |
401 else: |
417 else: |
402 callTraceEnabled = None |
418 callTraceEnabled = None |
403 |
419 |
404 if self.debugging: |
420 if self.debugging: |
405 sys.setprofile(callTraceEnabled) |
421 sys.setprofile(callTraceEnabled) |
406 else: |
422 else: |
407 # remember for later |
423 # remember for later |
408 self.callTraceEnabled = callTraceEnabled |
424 self.callTraceEnabled = callTraceEnabled |
409 |
425 |
410 elif method == "RequestEnvironment": |
426 elif method == "RequestEnvironment": |
411 for key, value in params["environment"].items(): |
427 for key, value in params["environment"].items(): |
412 if key.endswith("+"): |
428 if key.endswith("+"): |
413 # append to the key |
429 # append to the key |
414 key = key[:-1] |
430 key = key[:-1] |
421 key = key[:-1] |
437 key = key[:-1] |
422 if key in os.environ: |
438 if key in os.environ: |
423 del os.environ[key] |
439 del os.environ[key] |
424 else: |
440 else: |
425 os.environ[key] = value |
441 os.environ[key] = value |
426 |
442 |
427 elif method == "RequestLoad": |
443 elif method == "RequestLoad": |
428 self._fncache = {} |
444 self._fncache = {} |
429 self.dircache = [] |
445 self.dircache = [] |
430 self.disassembly = None |
446 self.disassembly = None |
431 sys.argv = [] |
447 sys.argv = [] |
432 self.__setCoding(params["filename"]) |
448 self.__setCoding(params["filename"]) |
433 sys.argv.append(params["filename"]) |
449 sys.argv.append(params["filename"]) |
434 sys.argv.extend(params["argv"]) |
450 sys.argv.extend(params["argv"]) |
435 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
451 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
436 if params["workdir"] == '': |
452 if params["workdir"] == "": |
437 os.chdir(sys.path[1]) |
453 os.chdir(sys.path[1]) |
438 else: |
454 else: |
439 os.chdir(params["workdir"]) |
455 os.chdir(params["workdir"]) |
440 |
456 |
441 self.running = sys.argv[0] |
457 self.running = sys.argv[0] |
442 self.debugging = True |
458 self.debugging = True |
443 self.multiprocessSupport = params["multiprocess"] |
459 self.multiprocessSupport = params["multiprocess"] |
444 |
460 |
445 self.threads.clear() |
461 self.threads.clear() |
446 self.attachThread(mainThread=True) |
462 self.attachThread(mainThread=True) |
447 |
463 |
448 # set the system exception handling function to ensure, that |
464 # set the system exception handling function to ensure, that |
449 # we report on all unhandled exceptions |
465 # we report on all unhandled exceptions |
450 sys.excepthook = self.__unhandled_exception |
466 sys.excepthook = self.__unhandled_exception |
451 self.__interceptSignals() |
467 self.__interceptSignals() |
452 |
468 |
453 # clear all old breakpoints, they'll get set after we have |
469 # clear all old breakpoints, they'll get set after we have |
454 # started |
470 # started |
455 Breakpoint.clear_all_breaks() |
471 Breakpoint.clear_all_breaks() |
456 Watch.clear_all_watches() |
472 Watch.clear_all_watches() |
457 |
473 |
458 self.mainThread.tracePythonLibs(params["traceInterpreter"]) |
474 self.mainThread.tracePythonLibs(params["traceInterpreter"]) |
459 |
475 |
460 # This will eventually enter a local event loop. |
476 # This will eventually enter a local event loop. |
461 self.debugMod.__dict__['__file__'] = self.running |
477 self.debugMod.__dict__["__file__"] = self.running |
462 sys.modules['__main__'] = self.debugMod |
478 sys.modules["__main__"] = self.debugMod |
463 code = self.__compileFileSource(self.running) |
479 code = self.__compileFileSource(self.running) |
464 if code: |
480 if code: |
465 sys.setprofile(self.callTraceEnabled) |
481 sys.setprofile(self.callTraceEnabled) |
466 self.mainThread.run(code, self.debugMod.__dict__, debug=True, |
482 self.mainThread.run( |
467 closeSession=False) |
483 code, self.debugMod.__dict__, debug=True, closeSession=False |
|
484 ) |
468 |
485 |
469 elif method == "RequestRun": |
486 elif method == "RequestRun": |
470 self.disassembly = None |
487 self.disassembly = None |
471 sys.argv = [] |
488 sys.argv = [] |
472 self.__setCoding(params["filename"]) |
489 self.__setCoding(params["filename"]) |
473 sys.argv.append(params["filename"]) |
490 sys.argv.append(params["filename"]) |
474 sys.argv.extend(params["argv"]) |
491 sys.argv.extend(params["argv"]) |
475 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
492 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
476 if params["workdir"] == '': |
493 if params["workdir"] == "": |
477 os.chdir(sys.path[1]) |
494 os.chdir(sys.path[1]) |
478 else: |
495 else: |
479 os.chdir(params["workdir"]) |
496 os.chdir(params["workdir"]) |
480 |
497 |
481 self.running = sys.argv[0] |
498 self.running = sys.argv[0] |
482 self.botframe = None |
499 self.botframe = None |
483 |
500 |
484 self.threads.clear() |
501 self.threads.clear() |
485 self.attachThread(mainThread=True) |
502 self.attachThread(mainThread=True) |
486 |
503 |
487 # set the system exception handling function to ensure, that |
504 # set the system exception handling function to ensure, that |
488 # we report on all unhandled exceptions |
505 # we report on all unhandled exceptions |
489 sys.excepthook = self.__unhandled_exception |
506 sys.excepthook = self.__unhandled_exception |
490 self.__interceptSignals() |
507 self.__interceptSignals() |
491 |
508 |
492 self.mainThread.tracePythonLibs(False) |
509 self.mainThread.tracePythonLibs(False) |
493 |
510 |
494 self.debugMod.__dict__['__file__'] = sys.argv[0] |
511 self.debugMod.__dict__["__file__"] = sys.argv[0] |
495 sys.modules['__main__'] = self.debugMod |
512 sys.modules["__main__"] = self.debugMod |
496 res = 0 |
513 res = 0 |
497 code = self.__compileFileSource(self.running) |
514 code = self.__compileFileSource(self.running) |
498 if code: |
515 if code: |
499 self.mainThread.run(code, self.debugMod.__dict__, debug=False, |
516 self.mainThread.run( |
500 closeSession=False) |
517 code, self.debugMod.__dict__, debug=False, closeSession=False |
|
518 ) |
501 |
519 |
502 elif method == "RequestCoverage": |
520 elif method == "RequestCoverage": |
503 from coverage import Coverage |
521 from coverage import Coverage |
|
522 |
504 self.disassembly = None |
523 self.disassembly = None |
505 sys.argv = [] |
524 sys.argv = [] |
506 self.__setCoding(params["filename"]) |
525 self.__setCoding(params["filename"]) |
507 sys.argv.append(params["filename"]) |
526 sys.argv.append(params["filename"]) |
508 sys.argv.extend(params["argv"]) |
527 sys.argv.extend(params["argv"]) |
509 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
528 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
510 if params["workdir"] == '': |
529 if params["workdir"] == "": |
511 os.chdir(sys.path[1]) |
530 os.chdir(sys.path[1]) |
512 else: |
531 else: |
513 os.chdir(params["workdir"]) |
532 os.chdir(params["workdir"]) |
514 |
533 |
515 # set the system exception handling function to ensure, that |
534 # set the system exception handling function to ensure, that |
516 # we report on all unhandled exceptions |
535 # we report on all unhandled exceptions |
517 sys.excepthook = self.__unhandled_exception |
536 sys.excepthook = self.__unhandled_exception |
518 self.__interceptSignals() |
537 self.__interceptSignals() |
519 |
538 |
520 # generate a coverage object |
539 # generate a coverage object |
521 self.cover = Coverage( |
540 self.cover = Coverage( |
522 auto_data=True, |
541 auto_data=True, |
523 data_file="{0}.coverage".format( |
542 data_file="{0}.coverage".format(os.path.splitext(sys.argv[0])[0]), |
524 os.path.splitext(sys.argv[0])[0])) |
543 ) |
525 |
544 |
526 if params["erase"]: |
545 if params["erase"]: |
527 self.cover.erase() |
546 self.cover.erase() |
528 sys.modules['__main__'] = self.debugMod |
547 sys.modules["__main__"] = self.debugMod |
529 self.debugMod.__dict__['__file__'] = sys.argv[0] |
548 self.debugMod.__dict__["__file__"] = sys.argv[0] |
530 code = self.__compileFileSource(sys.argv[0]) |
549 code = self.__compileFileSource(sys.argv[0]) |
531 if code: |
550 if code: |
532 self.running = sys.argv[0] |
551 self.running = sys.argv[0] |
533 self.cover.start() |
552 self.cover.start() |
534 self.mainThread.run(code, self.debugMod.__dict__, debug=False, |
553 self.mainThread.run( |
535 closeSession=False) |
554 code, self.debugMod.__dict__, debug=False, closeSession=False |
|
555 ) |
536 self.cover.stop() |
556 self.cover.stop() |
537 self.cover.save() |
557 self.cover.save() |
538 |
558 |
539 elif method == "RequestProfile": |
559 elif method == "RequestProfile": |
540 sys.setprofile(None) |
560 sys.setprofile(None) |
541 import PyProfile |
561 import PyProfile |
|
562 |
542 self.disassembly = None |
563 self.disassembly = None |
543 sys.argv = [] |
564 sys.argv = [] |
544 self.__setCoding(params["filename"]) |
565 self.__setCoding(params["filename"]) |
545 sys.argv.append(params["filename"]) |
566 sys.argv.append(params["filename"]) |
546 sys.argv.extend(params["argv"]) |
567 sys.argv.extend(params["argv"]) |
547 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
568 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
548 if params["workdir"] == '': |
569 if params["workdir"] == "": |
549 os.chdir(sys.path[1]) |
570 os.chdir(sys.path[1]) |
550 else: |
571 else: |
551 os.chdir(params["workdir"]) |
572 os.chdir(params["workdir"]) |
552 |
573 |
553 # set the system exception handling function to ensure, that |
574 # set the system exception handling function to ensure, that |
554 # we report on all unhandled exceptions |
575 # we report on all unhandled exceptions |
555 sys.excepthook = self.__unhandled_exception |
576 sys.excepthook = self.__unhandled_exception |
556 self.__interceptSignals() |
577 self.__interceptSignals() |
557 |
578 |
558 # generate a profile object |
579 # generate a profile object |
559 self.prof = PyProfile.PyProfile(sys.argv[0]) |
580 self.prof = PyProfile.PyProfile(sys.argv[0]) |
560 |
581 |
561 if params["erase"]: |
582 if params["erase"]: |
562 self.prof.erase() |
583 self.prof.erase() |
563 self.debugMod.__dict__['__file__'] = sys.argv[0] |
584 self.debugMod.__dict__["__file__"] = sys.argv[0] |
564 sys.modules['__main__'] = self.debugMod |
585 sys.modules["__main__"] = self.debugMod |
565 script = '' |
586 script = "" |
566 with codecs.open(sys.argv[0], encoding=self.__coding) as fp: |
587 with codecs.open(sys.argv[0], encoding=self.__coding) as fp: |
567 script = fp.read() |
588 script = fp.read() |
568 if script and not script.endswith('\n'): |
589 if script and not script.endswith("\n"): |
569 script += '\n' |
590 script += "\n" |
570 |
591 |
571 if script: |
592 if script: |
572 self.running = sys.argv[0] |
593 self.running = sys.argv[0] |
573 res = 0 |
594 res = 0 |
574 try: |
595 try: |
575 self.prof.run(script) |
596 self.prof.run(script) |
668 sys.last_traceback = exc_tb |
692 sys.last_traceback = exc_tb |
669 tblist = traceback.extract_tb(exc_tb) |
693 tblist = traceback.extract_tb(exc_tb) |
670 del tblist[:1] |
694 del tblist[:1] |
671 tlist = traceback.format_list(tblist) |
695 tlist = traceback.format_list(tblist) |
672 if tlist: |
696 if tlist: |
673 tlist.insert( |
697 tlist.insert(0, "Traceback (innermost last):\n") |
674 0, "Traceback (innermost last):\n") |
698 tlist.extend( |
675 tlist.extend(traceback.format_exception_only( |
699 traceback.format_exception_only(exc_type, exc_value) |
676 exc_type, exc_value)) |
700 ) |
677 finally: |
701 finally: |
678 tblist = exc_tb = None |
702 tblist = exc_tb = None |
679 |
703 |
680 self.sendJsonCommand("ClientOutput", { |
704 self.sendJsonCommand("ClientOutput", {"text": "".join(tlist)}) |
681 "text": "".join(tlist) |
705 |
682 }) |
|
683 |
|
684 self.sendJsonCommand("ResponseOK", {}) |
706 self.sendJsonCommand("ResponseOK", {}) |
685 |
707 |
686 elif method == "RequestStep": |
708 elif method == "RequestStep": |
687 self.currentThreadExec.step(True) |
709 self.currentThreadExec.step(True) |
688 self.eventExit = True |
710 self.eventExit = True |
689 |
711 |
690 elif method == "RequestStepOver": |
712 elif method == "RequestStepOver": |
691 self.currentThreadExec.step(False) |
713 self.currentThreadExec.step(False) |
692 self.eventExit = True |
714 self.eventExit = True |
693 |
715 |
694 elif method == "RequestStepOut": |
716 elif method == "RequestStepOut": |
695 self.currentThreadExec.stepOut() |
717 self.currentThreadExec.stepOut() |
696 self.eventExit = True |
718 self.eventExit = True |
697 |
719 |
698 elif method == "RequestStepQuit": |
720 elif method == "RequestStepQuit": |
699 if self.passive: |
721 if self.passive: |
700 self.progTerminated(42) |
722 self.progTerminated(42) |
701 else: |
723 else: |
702 self.set_quit() |
724 self.set_quit() |
703 self.eventExit = True |
725 self.eventExit = True |
704 |
726 |
705 elif method == "RequestMoveIP": |
727 elif method == "RequestMoveIP": |
706 newLine = params["newLine"] |
728 newLine = params["newLine"] |
707 self.currentThreadExec.move_instruction_pointer(newLine) |
729 self.currentThreadExec.move_instruction_pointer(newLine) |
708 |
730 |
709 elif method == "RequestContinue": |
731 elif method == "RequestContinue": |
710 self.currentThreadExec.go(params["special"]) |
732 self.currentThreadExec.go(params["special"]) |
711 self.eventExit = True |
733 self.eventExit = True |
712 |
734 |
713 elif method == "RequestContinueUntil": |
735 elif method == "RequestContinueUntil": |
714 newLine = params["newLine"] |
736 newLine = params["newLine"] |
715 self.currentThreadExec.set_until(lineno=newLine) |
737 self.currentThreadExec.set_until(lineno=newLine) |
716 self.eventExit = True |
738 self.eventExit = True |
717 |
739 |
718 elif method == "RawInput": |
740 elif method == "RawInput": |
719 # If we are handling raw mode input then break out of the current |
741 # If we are handling raw mode input then break out of the current |
720 # event loop. |
742 # event loop. |
721 self.rawLine = params["input"] |
743 self.rawLine = params["input"] |
722 self.eventExit = True |
744 self.eventExit = True |
723 |
745 |
724 elif method == "RequestBreakpoint": |
746 elif method == "RequestBreakpoint": |
725 if params["setBreakpoint"]: |
747 if params["setBreakpoint"]: |
726 if params["condition"] in ['None', '']: |
748 if params["condition"] in ["None", ""]: |
727 cond = None |
749 cond = None |
728 elif params["condition"] is not None: |
750 elif params["condition"] is not None: |
729 try: |
751 try: |
730 cond = compile(params["condition"], '<string>', 'eval') |
752 cond = compile(params["condition"], "<string>", "eval") |
731 except SyntaxError: |
753 except SyntaxError: |
732 self.sendJsonCommand("ResponseBPConditionError", { |
754 self.sendJsonCommand( |
733 "filename": params["filename"], |
755 "ResponseBPConditionError", |
734 "line": params["line"], |
756 { |
735 }) |
757 "filename": params["filename"], |
|
758 "line": params["line"], |
|
759 }, |
|
760 ) |
736 return |
761 return |
737 else: |
762 else: |
738 cond = None |
763 cond = None |
739 |
764 |
740 Breakpoint( |
765 Breakpoint( |
741 params["filename"], params["line"], params["temporary"], |
766 params["filename"], params["line"], params["temporary"], cond |
742 cond) |
767 ) |
743 else: |
768 else: |
744 Breakpoint.clear_break(params["filename"], params["line"]) |
769 Breakpoint.clear_break(params["filename"], params["line"]) |
745 |
770 |
746 elif method == "RequestBreakpointEnable": |
771 elif method == "RequestBreakpointEnable": |
747 bp = Breakpoint.get_break(params["filename"], params["line"]) |
772 bp = Breakpoint.get_break(params["filename"], params["line"]) |
748 if bp is not None: |
773 if bp is not None: |
749 if params["enable"]: |
774 if params["enable"]: |
750 bp.enable() |
775 bp.enable() |
751 else: |
776 else: |
752 bp.disable() |
777 bp.disable() |
753 |
778 |
754 elif method == "RequestBreakpointIgnore": |
779 elif method == "RequestBreakpointIgnore": |
755 bp = Breakpoint.get_break(params["filename"], params["line"]) |
780 bp = Breakpoint.get_break(params["filename"], params["line"]) |
756 if bp is not None: |
781 if bp is not None: |
757 bp.ignore = params["count"] |
782 bp.ignore = params["count"] |
758 |
783 |
759 elif method == "RequestWatch": |
784 elif method == "RequestWatch": |
760 if params["setWatch"]: |
785 if params["setWatch"]: |
761 if params["condition"].endswith( |
786 if params["condition"].endswith(("??created??", "??changed??")): |
762 ('??created??', '??changed??')): |
|
763 compiledCond, flag = params["condition"].split() |
787 compiledCond, flag = params["condition"].split() |
764 else: |
788 else: |
765 compiledCond = params["condition"] |
789 compiledCond = params["condition"] |
766 flag = '' |
790 flag = "" |
767 |
791 |
768 try: |
792 try: |
769 compiledCond = compile(compiledCond, '<string>', 'eval') |
793 compiledCond = compile(compiledCond, "<string>", "eval") |
770 except SyntaxError: |
794 except SyntaxError: |
771 self.sendJsonCommand("ResponseWatchConditionError", { |
795 self.sendJsonCommand( |
772 "condition": params["condition"], |
796 "ResponseWatchConditionError", |
773 }) |
797 { |
|
798 "condition": params["condition"], |
|
799 }, |
|
800 ) |
774 return |
801 return |
775 Watch( |
802 Watch(params["condition"], compiledCond, flag, params["temporary"]) |
776 params["condition"], compiledCond, flag, |
|
777 params["temporary"]) |
|
778 else: |
803 else: |
779 Watch.clear_watch(params["condition"]) |
804 Watch.clear_watch(params["condition"]) |
780 |
805 |
781 elif method == "RequestWatchEnable": |
806 elif method == "RequestWatchEnable": |
782 wp = Watch.get_watch(params["condition"]) |
807 wp = Watch.get_watch(params["condition"]) |
783 if wp is not None: |
808 if wp is not None: |
784 if params["enable"]: |
809 if params["enable"]: |
785 wp.enable() |
810 wp.enable() |
786 else: |
811 else: |
787 wp.disable() |
812 wp.disable() |
788 |
813 |
789 elif method == "RequestWatchIgnore": |
814 elif method == "RequestWatchIgnore": |
790 wp = Watch.get_watch(params["condition"]) |
815 wp = Watch.get_watch(params["condition"]) |
791 if wp is not None: |
816 if wp is not None: |
792 wp.ignore = params["count"] |
817 wp.ignore = params["count"] |
793 |
818 |
794 elif method == "RequestShutdown": |
819 elif method == "RequestShutdown": |
795 self.sessionClose() |
820 self.sessionClose() |
796 |
821 |
797 elif method == "RequestSetNoDebugList": |
822 elif method == "RequestSetNoDebugList": |
798 self.noDebugList = params["noDebug"][:] |
823 self.noDebugList = params["noDebug"][:] |
799 |
824 |
800 elif method == "RequestCompletion": |
825 elif method == "RequestCompletion": |
801 self.__completionList(params["text"]) |
826 self.__completionList(params["text"]) |
802 |
827 |
803 def setDisassembly(self, disassembly): |
828 def setDisassembly(self, disassembly): |
804 """ |
829 """ |
805 Public method to store a disassembly of the code object raising an |
830 Public method to store a disassembly of the code object raising an |
806 exception. |
831 exception. |
807 |
832 |
808 @param disassembly dictionary containing the disassembly information |
833 @param disassembly dictionary containing the disassembly information |
809 @type dict |
834 @type dict |
810 """ |
835 """ |
811 self.disassembly = disassembly |
836 self.disassembly = disassembly |
812 |
837 |
813 def sendJsonCommand(self, method, params): |
838 def sendJsonCommand(self, method, params): |
814 """ |
839 """ |
815 Public method to send a single command or response to the IDE. |
840 Public method to send a single command or response to the IDE. |
816 |
841 |
817 @param method command or response command name to be sent |
842 @param method command or response command name to be sent |
818 @type str |
843 @type str |
819 @param params dictionary of named parameters for the command or |
844 @param params dictionary of named parameters for the command or |
820 response |
845 response |
821 @type dict |
846 @type dict |
822 """ |
847 """ |
823 # send debugger ID with all responses |
848 # send debugger ID with all responses |
824 if "debuggerId" not in params: |
849 if "debuggerId" not in params: |
825 params["debuggerId"] = self.__debuggerId |
850 params["debuggerId"] = self.__debuggerId |
826 |
851 |
827 cmd = prepareJsonCommand(method, params) |
852 cmd = prepareJsonCommand(method, params) |
828 |
853 |
829 self.writestream.write_p(cmd) |
854 self.writestream.write_p(cmd) |
830 self.writestream.flush() |
855 self.writestream.flush() |
831 |
856 |
832 def sendClearTemporaryBreakpoint(self, filename, lineno): |
857 def sendClearTemporaryBreakpoint(self, filename, lineno): |
833 """ |
858 """ |
834 Public method to signal the deletion of a temporary breakpoint. |
859 Public method to signal the deletion of a temporary breakpoint. |
835 |
860 |
836 @param filename name of the file the bp belongs to |
861 @param filename name of the file the bp belongs to |
837 @type str |
862 @type str |
838 @param lineno linenumber of the bp |
863 @param lineno linenumber of the bp |
839 @type int |
864 @type int |
840 """ |
865 """ |
841 self.sendJsonCommand("ResponseClearBreakpoint", { |
866 self.sendJsonCommand( |
842 "filename": filename, |
867 "ResponseClearBreakpoint", {"filename": filename, "line": lineno} |
843 "line": lineno |
868 ) |
844 }) |
869 |
845 |
|
846 def sendClearTemporaryWatch(self, condition): |
870 def sendClearTemporaryWatch(self, condition): |
847 """ |
871 """ |
848 Public method to signal the deletion of a temporary watch expression. |
872 Public method to signal the deletion of a temporary watch expression. |
849 |
873 |
850 @param condition condition of the watch expression to be cleared |
874 @param condition condition of the watch expression to be cleared |
851 @type str |
875 @type str |
852 """ |
876 """ |
853 self.sendJsonCommand("ResponseClearWatch", { |
877 self.sendJsonCommand( |
854 "condition": condition, |
878 "ResponseClearWatch", |
855 }) |
879 { |
856 |
880 "condition": condition, |
|
881 }, |
|
882 ) |
|
883 |
857 def sendResponseLine(self, stack, threadName): |
884 def sendResponseLine(self, stack, threadName): |
858 """ |
885 """ |
859 Public method to send the current call stack. |
886 Public method to send the current call stack. |
860 |
887 |
861 @param stack call stack |
888 @param stack call stack |
862 @type list |
889 @type list |
863 @param threadName name of the thread sending the event |
890 @param threadName name of the thread sending the event |
864 @type str |
891 @type str |
865 """ |
892 """ |
866 self.sendJsonCommand("ResponseLine", { |
893 self.sendJsonCommand( |
867 "stack": stack, |
894 "ResponseLine", |
868 "threadName": threadName, |
895 { |
869 }) |
896 "stack": stack, |
870 |
897 "threadName": threadName, |
|
898 }, |
|
899 ) |
|
900 |
871 def sendCallTrace(self, event, fromInfo, toInfo): |
901 def sendCallTrace(self, event, fromInfo, toInfo): |
872 """ |
902 """ |
873 Public method to send a call trace entry. |
903 Public method to send a call trace entry. |
874 |
904 |
875 @param event trace event (call or return) |
905 @param event trace event (call or return) |
876 @type str |
906 @type str |
877 @param fromInfo dictionary containing the origin info |
907 @param fromInfo dictionary containing the origin info |
878 @type dict with 'filename', 'linenumber' and 'codename' |
908 @type dict with 'filename', 'linenumber' and 'codename' |
879 as keys |
909 as keys |
880 @param toInfo dictionary containing the target info |
910 @param toInfo dictionary containing the target info |
881 @type dict with 'filename', 'linenumber' and 'codename' |
911 @type dict with 'filename', 'linenumber' and 'codename' |
882 as keys |
912 as keys |
883 """ |
913 """ |
884 self.sendJsonCommand("CallTrace", { |
914 self.sendJsonCommand( |
885 "event": event[0], |
915 "CallTrace", |
886 "from": fromInfo, |
916 { |
887 "to": toInfo, |
917 "event": event[0], |
888 }) |
918 "from": fromInfo, |
889 |
919 "to": toInfo, |
890 def sendException(self, exceptionType, exceptionMessage, stack, |
920 }, |
891 threadName): |
921 ) |
|
922 |
|
923 def sendException(self, exceptionType, exceptionMessage, stack, threadName): |
892 """ |
924 """ |
893 Public method to send information for an exception. |
925 Public method to send information for an exception. |
894 |
926 |
895 @param exceptionType type of exception raised |
927 @param exceptionType type of exception raised |
896 @type str |
928 @type str |
897 @param exceptionMessage message of the exception |
929 @param exceptionMessage message of the exception |
898 @type str |
930 @type str |
899 @param stack stack trace information |
931 @param stack stack trace information |
900 @type list |
932 @type list |
901 @param threadName name of the thread sending the event |
933 @param threadName name of the thread sending the event |
902 @type str |
934 @type str |
903 """ |
935 """ |
904 self.sendJsonCommand("ResponseException", { |
936 self.sendJsonCommand( |
905 "type": exceptionType, |
937 "ResponseException", |
906 "message": exceptionMessage, |
938 { |
907 "stack": stack, |
939 "type": exceptionType, |
908 "threadName": threadName, |
940 "message": exceptionMessage, |
909 }) |
941 "stack": stack, |
910 |
942 "threadName": threadName, |
|
943 }, |
|
944 ) |
|
945 |
911 def sendSyntaxError(self, message, filename, lineno, charno, threadName): |
946 def sendSyntaxError(self, message, filename, lineno, charno, threadName): |
912 """ |
947 """ |
913 Public method to send information for a syntax error. |
948 Public method to send information for a syntax error. |
914 |
949 |
915 @param message syntax error message |
950 @param message syntax error message |
916 @type str |
951 @type str |
917 @param filename name of the faulty file |
952 @param filename name of the faulty file |
918 @type str |
953 @type str |
919 @param lineno line number info |
954 @param lineno line number info |
921 @param charno character number info |
956 @param charno character number info |
922 @type int |
957 @type int |
923 @param threadName name of the thread sending the event |
958 @param threadName name of the thread sending the event |
924 @type str |
959 @type str |
925 """ |
960 """ |
926 self.sendJsonCommand("ResponseSyntax", { |
961 self.sendJsonCommand( |
927 "message": message, |
962 "ResponseSyntax", |
928 "filename": filename, |
963 { |
929 "linenumber": lineno, |
964 "message": message, |
930 "characternumber": charno, |
965 "filename": filename, |
931 "threadName": threadName, |
966 "linenumber": lineno, |
932 }) |
967 "characternumber": charno, |
933 |
968 "threadName": threadName, |
|
969 }, |
|
970 ) |
|
971 |
934 def sendPassiveStartup(self, filename, exceptions): |
972 def sendPassiveStartup(self, filename, exceptions): |
935 """ |
973 """ |
936 Public method to send the passive start information. |
974 Public method to send the passive start information. |
937 |
975 |
938 @param filename name of the script |
976 @param filename name of the script |
939 @type str |
977 @type str |
940 @param exceptions flag to enable exception reporting of the IDE |
978 @param exceptions flag to enable exception reporting of the IDE |
941 @type bool |
979 @type bool |
942 """ |
980 """ |
943 self.sendJsonCommand("PassiveStartup", { |
981 self.sendJsonCommand( |
944 "filename": filename, |
982 "PassiveStartup", |
945 "exceptions": exceptions, |
983 { |
946 }) |
984 "filename": filename, |
947 |
985 "exceptions": exceptions, |
|
986 }, |
|
987 ) |
|
988 |
948 def sendDebuggerId(self, debuggerId): |
989 def sendDebuggerId(self, debuggerId): |
949 """ |
990 """ |
950 Public method to send the debug client id. |
991 Public method to send the debug client id. |
951 |
992 |
952 @param debuggerId id of this debug client instance (made up of |
993 @param debuggerId id of this debug client instance (made up of |
953 hostname and process ID) |
994 hostname and process ID) |
954 @type str |
995 @type str |
955 """ |
996 """ |
956 # debugger ID is added automatically by sendJsonCommand |
997 # debugger ID is added automatically by sendJsonCommand |
957 self.sendJsonCommand("DebuggerId", {}) |
998 self.sendJsonCommand("DebuggerId", {}) |
958 |
999 |
959 def __clientCapabilities(self): |
1000 def __clientCapabilities(self): |
960 """ |
1001 """ |
961 Private method to determine the clients capabilities. |
1002 Private method to determine the clients capabilities. |
962 |
1003 |
963 @return client capabilities (integer) |
1004 @return client capabilities (integer) |
964 """ |
1005 """ |
965 try: |
1006 try: |
966 import PyProfile # __IGNORE_WARNING__ |
1007 import PyProfile # __IGNORE_WARNING__ |
|
1008 |
967 with contextlib.suppress(KeyError): |
1009 with contextlib.suppress(KeyError): |
968 del sys.modules['PyProfile'] |
1010 del sys.modules["PyProfile"] |
969 return self.clientCapabilities |
1011 return self.clientCapabilities |
970 except ImportError: |
1012 except ImportError: |
971 return ( |
1013 return self.clientCapabilities & ~DebugClientCapabilities.HasProfiler |
972 self.clientCapabilities & ~DebugClientCapabilities.HasProfiler) |
1014 |
973 |
|
974 def readReady(self, stream): |
1015 def readReady(self, stream): |
975 """ |
1016 """ |
976 Public method called when there is data ready to be read. |
1017 Public method called when there is data ready to be read. |
977 |
1018 |
978 @param stream file like object that has data to be read |
1019 @param stream file like object that has data to be read |
979 @return flag indicating an error condition |
1020 @return flag indicating an error condition |
980 @rtype bool |
1021 @rtype bool |
981 """ |
1022 """ |
982 error = False |
1023 error = False |
983 |
1024 |
984 self.lockClient() |
1025 self.lockClient() |
985 try: |
1026 try: |
986 command = stream.readCommand() |
1027 command = stream.readCommand() |
987 except Exception: |
1028 except Exception: |
988 error = True |
1029 error = True |
1123 if remoteAddress is None: |
1163 if remoteAddress is None: |
1124 remoteAddress = "127.0.0.1" |
1164 remoteAddress = "127.0.0.1" |
1125 elif "@@i" in remoteAddress: |
1165 elif "@@i" in remoteAddress: |
1126 remoteAddress = remoteAddress.split("@@i")[0] |
1166 remoteAddress = remoteAddress.split("@@i")[0] |
1127 sock = socket.create_connection((remoteAddress, port)) |
1167 sock = socket.create_connection((remoteAddress, port)) |
1128 |
1168 |
1129 stdinName = sys.stdin.name |
1169 stdinName = sys.stdin.name |
1130 # Special case if in a multiprocessing.Process |
1170 # Special case if in a multiprocessing.Process |
1131 if isinstance(stdinName, int): |
1171 if isinstance(stdinName, int): |
1132 stdinName = '<stdin>' |
1172 stdinName = "<stdin>" |
1133 |
1173 |
1134 self.readstream = AsyncFile(sock, sys.stdin.mode, stdinName) |
1174 self.readstream = AsyncFile(sock, sys.stdin.mode, stdinName) |
1135 self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name) |
1175 self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name) |
1136 self.errorstream = AsyncFile(sock, sys.stderr.mode, sys.stderr.name) |
1176 self.errorstream = AsyncFile(sock, sys.stderr.mode, sys.stderr.name) |
1137 |
1177 |
1138 if redirect: |
1178 if redirect: |
1139 sys.stdin = self.readstream |
1179 sys.stdin = self.readstream |
1140 sys.stdout = self.writestream |
1180 sys.stdout = self.writestream |
1141 sys.stderr = self.errorstream |
1181 sys.stderr = self.errorstream |
1142 self.redirect = redirect |
1182 self.redirect = redirect |
1143 |
1183 |
1144 # attach to the main thread here |
1184 # attach to the main thread here |
1145 self.attachThread(mainThread=True) |
1185 self.attachThread(mainThread=True) |
1146 |
1186 |
1147 if not name: |
1187 if not name: |
1148 name = "main" |
1188 name = "main" |
1149 self.__debuggerId = "{0}/{1}/{2}".format( |
1189 self.__debuggerId = "{0}/{1}/{2}".format( |
1150 socket.gethostname(), os.getpid(), name |
1190 socket.gethostname(), os.getpid(), name |
1151 ) |
1191 ) |
1152 |
1192 |
1153 self.sendDebuggerId(self.__debuggerId) |
1193 self.sendDebuggerId(self.__debuggerId) |
1154 |
1194 |
1155 def __unhandled_exception(self, exctype, excval, exctb): |
1195 def __unhandled_exception(self, exctype, excval, exctb): |
1156 """ |
1196 """ |
1157 Private method called to report an uncaught exception. |
1197 Private method called to report an uncaught exception. |
1158 |
1198 |
1159 @param exctype the type of the exception |
1199 @param exctype the type of the exception |
1160 @param excval data about the exception |
1200 @param excval data about the exception |
1161 @param exctb traceback for the exception |
1201 @param exctb traceback for the exception |
1162 """ |
1202 """ |
1163 self.mainThread.user_exception((exctype, excval, exctb), True) |
1203 self.mainThread.user_exception((exctype, excval, exctb), True) |
1164 |
1204 |
1165 def __interceptSignals(self): |
1205 def __interceptSignals(self): |
1166 """ |
1206 """ |
1167 Private method to intercept common signals. |
1207 Private method to intercept common signals. |
1168 """ |
1208 """ |
1169 for signum in [ |
1209 for signum in [ |
1170 signal.SIGABRT, # abnormal termination |
1210 signal.SIGABRT, # abnormal termination |
1171 signal.SIGFPE, # floating point exception |
1211 signal.SIGFPE, # floating point exception |
1172 signal.SIGILL, # illegal instruction |
1212 signal.SIGILL, # illegal instruction |
1173 signal.SIGSEGV, # segmentation violation |
1213 signal.SIGSEGV, # segmentation violation |
1174 ]: |
1214 ]: |
1175 signal.signal(signum, self.__signalHandler) |
1215 signal.signal(signum, self.__signalHandler) |
1176 |
1216 |
1177 def __signalHandler(self, signalNumber, stackFrame): |
1217 def __signalHandler(self, signalNumber, stackFrame): |
1178 """ |
1218 """ |
1179 Private method to handle signals. |
1219 Private method to handle signals. |
1180 |
1220 |
1181 @param signalNumber number of the signal to be handled |
1221 @param signalNumber number of the signal to be handled |
1182 @type int |
1222 @type int |
1183 @param stackFrame current stack frame |
1223 @param stackFrame current stack frame |
1184 @type frame object |
1224 @type frame object |
1185 """ |
1225 """ |
1385 varDict = f.f_globals |
1437 varDict = f.f_globals |
1386 elif f.f_globals is f.f_locals: |
1438 elif f.f_globals is f.f_locals: |
1387 scope = -1 |
1439 scope = -1 |
1388 else: |
1440 else: |
1389 varDict = f.f_locals |
1441 varDict = f.f_locals |
1390 |
1442 |
1391 varlist = [] |
1443 varlist = [] |
1392 |
1444 |
1393 # fast path if variable was looked up before (see elif) |
1445 # fast path if variable was looked up before (see elif) |
1394 if scope != -1 and str(var) in self.resolverCache[scope]: |
1446 if scope != -1 and str(var) in self.resolverCache[scope]: |
1395 varGen = self.resolverCache[scope][str(var)] |
1447 varGen = self.resolverCache[scope][str(var)] |
1396 idx, varDict = next(varGen) |
1448 idx, varDict = next(varGen) |
1397 if idx != -2: # more elements available |
1449 if idx != -2: # more elements available |
1398 var.insert(0, idx) |
1450 var.insert(0, idx) |
1399 varlist = self.__formatVariablesList( |
1451 varlist = self.__formatVariablesList(varDict, scope, filterList) |
1400 varDict, scope, filterList) |
|
1401 elif scope != -1: |
1452 elif scope != -1: |
1402 variable = varDict |
1453 variable = varDict |
1403 # Lookup the wanted attribute |
1454 # Lookup the wanted attribute |
1404 for attribute in var: |
1455 for attribute in var: |
1405 resolver = DebugVariables.getResolver(variable) |
1456 resolver = DebugVariables.getResolver(variable) |
1406 if resolver: |
1457 if resolver: |
1407 variable = resolver.resolve(variable, attribute) |
1458 variable = resolver.resolve(variable, attribute) |
1408 if variable is None: |
1459 if variable is None: |
1409 break |
1460 break |
1410 |
1461 |
1411 else: |
1462 else: |
1412 break |
1463 break |
1413 |
1464 |
1414 idx = -3 # Requested variable doesn't exist anymore |
1465 idx = -3 # Requested variable doesn't exist anymore |
1415 # If found, get the details of attribute |
1466 # If found, get the details of attribute |
1416 if variable is not None: |
1467 if variable is not None: |
1417 resolver = DebugVariables.getResolver(variable) |
1468 resolver = DebugVariables.getResolver(variable) |
1418 if resolver: |
1469 if resolver: |
1419 varGen = resolver.getVariableList(variable) |
1470 varGen = resolver.getVariableList(variable) |
1420 # cache for next lookup |
1471 # cache for next lookup |
1421 self.resolverCache[scope][str(var)] = varGen |
1472 self.resolverCache[scope][str(var)] = varGen |
1422 |
1473 |
1423 idx, varDict = next(varGen) |
1474 idx, varDict = next(varGen) |
1424 if idx != -2: # more elements available |
1475 if idx != -2: # more elements available |
1425 varlist = self.__formatVariablesList( |
1476 varlist = self.__formatVariablesList(varDict, scope, filterList) |
1426 varDict, scope, filterList) |
1477 |
1427 |
|
1428 var.insert(0, idx) |
1478 var.insert(0, idx) |
1429 |
1479 |
1430 self.sendJsonCommand("ResponseVariable", { |
1480 self.sendJsonCommand( |
1431 "scope": scope, |
1481 "ResponseVariable", |
1432 "variable": var, |
1482 { |
1433 "variables": varlist, |
1483 "scope": scope, |
1434 }) |
1484 "variable": var, |
1435 |
1485 "variables": varlist, |
|
1486 }, |
|
1487 ) |
|
1488 |
1436 def __formatVariablesList(self, variables, scope, filterList=None): |
1489 def __formatVariablesList(self, variables, scope, filterList=None): |
1437 """ |
1490 """ |
1438 Private method to produce a formated variables list. |
1491 Private method to produce a formated variables list. |
1439 |
1492 |
1440 The dictionary passed in to it is scanned. Variables are |
1493 The dictionary passed in to it is scanned. Variables are |
1441 only added to the list, if their type is not contained |
1494 only added to the list, if their type is not contained |
1442 in the filter list and their name doesn't match any of the filter |
1495 in the filter list and their name doesn't match any of the filter |
1443 expressions. The formated variables list (a list of tuples of 3 |
1496 expressions. The formated variables list (a list of tuples of 3 |
1444 values) is returned. |
1497 values) is returned. |
1445 |
1498 |
1446 @param variables variables list to be processed |
1499 @param variables variables list to be processed |
1447 @type list of tuple of (str, Any) or (str, str, Any) |
1500 @type list of tuple of (str, Any) or (str, str, Any) |
1448 @param scope 1 to filter using the globals filter, 0 using the locals |
1501 @param scope 1 to filter using the globals filter, 0 using the locals |
1449 filter. |
1502 filter. |
1450 Variables are only added to the list, if their name do not match |
1503 Variables are only added to the list, if their name do not match |
1458 variable entry is a tuple of three elements, the variable name, |
1511 variable entry is a tuple of three elements, the variable name, |
1459 its type and value. |
1512 its type and value. |
1460 @rtype list of tuple of (str, str, str) |
1513 @rtype list of tuple of (str, str, str) |
1461 """ |
1514 """ |
1462 filterList = set(filterList or []) |
1515 filterList = set(filterList or []) |
1463 |
1516 |
1464 varlist = [] |
1517 varlist = [] |
1465 patternFilterObjects = ( |
1518 patternFilterObjects = ( |
1466 self.globalsFilterObjects |
1519 self.globalsFilterObjects if scope else self.localsFilterObjects |
1467 if scope else |
|
1468 self.localsFilterObjects |
|
1469 ) |
1520 ) |
1470 |
1521 |
1471 for variabel in variables: |
1522 for variabel in variables: |
1472 valtype = None |
1523 valtype = None |
1473 rvalue = None |
1524 rvalue = None |
1474 try: |
1525 try: |
1475 key, value = variabel |
1526 key, value = variabel |
1476 except ValueError: |
1527 except ValueError: |
1477 # Special case for some Qt variables, where the real type is |
1528 # Special case for some Qt variables, where the real type is |
1478 # overwritten |
1529 # overwritten |
1479 key, valtype, rvalue = variabel |
1530 key, valtype, rvalue = variabel |
1480 |
1531 |
1481 # filter based on the filter pattern |
1532 # filter based on the filter pattern |
1482 if patternFilterObjects and patternFilterObjects.match(str(key)): |
1533 if patternFilterObjects and patternFilterObjects.match(str(key)): |
1483 continue |
1534 continue |
1484 |
1535 |
1485 # filter hidden attributes (filter #0) |
1536 # filter hidden attributes (filter #0) |
1486 if '__' in filterList and str(key)[:2] == '__': |
1537 if "__" in filterList and str(key)[:2] == "__": |
1487 continue |
1538 continue |
1488 |
1539 |
1489 hasChildren = False |
1540 hasChildren = False |
1490 # special handling for '__builtins__' (it's way too big) |
1541 # special handling for '__builtins__' (it's way too big) |
1491 if key == '__builtins__': |
1542 if key == "__builtins__": |
1492 rvalue = '<module builtins (built-in)>' |
1543 rvalue = "<module builtins (built-in)>" |
1493 valtype = 'module' |
1544 valtype = "module" |
1494 if valtype in filterList: |
1545 if valtype in filterList: |
1495 continue |
1546 continue |
1496 elif ( |
1547 elif (key in SpecialAttributes and "special_attributes" in filterList) or ( |
1497 (key in SpecialAttributes and |
1548 key == "__hash__" and "builtin_function_or_method" in filterList |
1498 "special_attributes" in filterList) or |
|
1499 (key == "__hash__" and |
|
1500 "builtin_function_or_method" in filterList) |
|
1501 ): |
1549 ): |
1502 continue |
1550 continue |
1503 elif valtype is None: |
1551 elif valtype is None: |
1504 # valtypestr, e.g. class 'PyQt6.QtCore.QPoint' |
1552 # valtypestr, e.g. class 'PyQt6.QtCore.QPoint' |
1505 valtypestr = str(type(value)) |
1553 valtypestr = str(type(value)) |
1506 _, valtype = valtypestr.split(' ', 1) |
1554 _, valtype = valtypestr.split(" ", 1) |
1507 # valtype is the real type, e.g. PyQt6.QtCore.QPoint |
1555 # valtype is the real type, e.g. PyQt6.QtCore.QPoint |
1508 # valtype_filter is used for filtering, where the base class is |
1556 # valtype_filter is used for filtering, where the base class is |
1509 # also important |
1557 # also important |
1510 valtype = valtype_filter = valtype[1:-2] |
1558 valtype = valtype_filter = valtype[1:-2] |
1511 # Strip 'instance' to be equal with Python 3 |
1559 # Strip 'instance' to be equal with Python 3 |
1515 valtype_filter = "class" |
1563 valtype_filter = "class" |
1516 if valtype == "type": |
1564 if valtype == "type": |
1517 valtype = "class" |
1565 valtype = "class" |
1518 elif valtype == "method-wrapper": |
1566 elif valtype == "method-wrapper": |
1519 valtype = valtype_filter = "builtin_function_or_method" |
1567 valtype = valtype_filter = "builtin_function_or_method" |
1520 |
1568 |
1521 # Don't process variables which types are on filter list |
1569 # Don't process variables which types are on filter list |
1522 if ( |
1570 if ( |
1523 valtype_filter in filterList or |
1571 valtype_filter in filterList |
1524 (valtype_filter in ("sip.enumtype", "sip.wrappertype") and |
1572 or ( |
1525 'class' in filterList) or |
1573 valtype_filter in ("sip.enumtype", "sip.wrappertype") |
1526 (valtype_filter in ( |
1574 and "class" in filterList |
1527 "sip.methoddescriptor", "method_descriptor") and |
1575 ) |
1528 'method' in filterList) or |
1576 or ( |
1529 (valtype_filter in ("numpy.ndarray", "array.array") and |
1577 valtype_filter in ("sip.methoddescriptor", "method_descriptor") |
1530 'list' in filterList) or |
1578 and "method" in filterList |
1531 (valtype_filter == "django.MultiValueDict" and |
1579 ) |
1532 'dict' in filterList) or |
1580 or ( |
1533 'instance' in filterList |
1581 valtype_filter in ("numpy.ndarray", "array.array") |
|
1582 and "list" in filterList |
|
1583 ) |
|
1584 or ( |
|
1585 valtype_filter == "django.MultiValueDict" |
|
1586 and "dict" in filterList |
|
1587 ) |
|
1588 or "instance" in filterList |
1534 ): |
1589 ): |
1535 continue |
1590 continue |
1536 |
1591 |
1537 length = -2 |
1592 length = -2 |
1538 indicator = '' |
1593 indicator = "" |
1539 |
1594 |
1540 if valtype == 'str': |
1595 if valtype == "str": |
1541 rvalue = repr(value) |
1596 rvalue = repr(value) |
1542 length = len(rvalue) |
1597 length = len(rvalue) |
1543 elif valtype in NonExpandableTypes: |
1598 elif valtype in NonExpandableTypes: |
1544 rvalue = repr(value) |
1599 rvalue = repr(value) |
1545 |
1600 |
1546 if rvalue is not None: |
1601 if rvalue is not None: |
1547 varlist.append( |
1602 varlist.append((key, indicator, valtype, hasChildren, length, rvalue)) |
1548 (key, indicator, valtype, hasChildren, length, rvalue) |
|
1549 ) |
|
1550 continue |
1603 continue |
1551 |
1604 |
1552 try: |
1605 try: |
1553 for dtype in DebugVariables._ArrayTypes: |
1606 for dtype in DebugVariables._ArrayTypes: |
1554 if isinstance(value, dtype): |
1607 if isinstance(value, dtype): |
1555 try: |
1608 try: |
1556 length = len(value) |
1609 length = len(value) |
1557 except TypeError: |
1610 except TypeError: |
1558 length = -1 # Uninitialized array |
1611 length = -1 # Uninitialized array |
1559 |
1612 |
1560 dtype = str(dtype)[8:-2] |
1613 dtype = str(dtype)[8:-2] |
1561 # Standard array type indicators |
1614 # Standard array type indicators |
1562 indicator = self.Type2Indicators.get(dtype, '') |
1615 indicator = self.Type2Indicators.get(dtype, "") |
1563 |
1616 |
1564 # Special handling of some array types |
1617 # Special handling of some array types |
1565 if valtype == 'array.array': |
1618 if valtype == "array.array": |
1566 indicator = '[<{0}>]'.format(value.typecode) |
1619 indicator = "[<{0}>]".format(value.typecode) |
1567 elif valtype == 'collections.defaultdict': |
1620 elif valtype == "collections.defaultdict": |
1568 if value.default_factory is None: |
1621 if value.default_factory is None: |
1569 def_factory = "None" |
1622 def_factory = "None" |
1570 else: |
1623 else: |
1571 def_factory = value.default_factory.__name__ |
1624 def_factory = value.default_factory.__name__ |
1572 indicator = '{{:<{0}>}}'.format(def_factory) |
1625 indicator = "{{:<{0}>}}".format(def_factory) |
1573 elif valtype == "numpy.ndarray" and length > -1: |
1626 elif valtype == "numpy.ndarray" and length > -1: |
1574 length = "x".join(str(x) for x in value.shape) |
1627 length = "x".join(str(x) for x in value.shape) |
1575 elif valtype.endswith(".MultiValueDict"): |
1628 elif valtype.endswith(".MultiValueDict"): |
1576 indicator = "{:}" # __IGNORE_WARNING__ |
1629 indicator = "{:}" # __IGNORE_WARNING__ |
1577 valtype = "django.MultiValueDict" # shortened type |
1630 valtype = "django.MultiValueDict" # shortened type |
1578 break |
1631 break |
1579 else: |
1632 else: |
1580 rvalue = repr(value) |
1633 rvalue = repr(value) |
1581 |
1634 |
1582 hasChildren = True |
1635 hasChildren = True |
1583 except Exception: |
1636 except Exception: |
1584 rvalue = '' |
1637 rvalue = "" |
1585 |
1638 |
1586 varlist.append( |
1639 varlist.append((key, indicator, valtype, hasChildren, length, rvalue)) |
1587 (key, indicator, valtype, hasChildren, length, rvalue) |
1640 |
1588 ) |
|
1589 |
|
1590 return varlist |
1641 return varlist |
1591 |
1642 |
1592 def __generateFilterObjects(self, scope, filterString): |
1643 def __generateFilterObjects(self, scope, filterString): |
1593 """ |
1644 """ |
1594 Private slot to convert a filter string to a list of filter objects. |
1645 Private slot to convert a filter string to a list of filter objects. |
1595 |
1646 |
1596 @param scope 1 to generate filter for global variables, 0 for local |
1647 @param scope 1 to generate filter for global variables, 0 for local |
1597 variables (int) |
1648 variables (int) |
1598 @param filterString string of filter patterns separated by ';' |
1649 @param filterString string of filter patterns separated by ';' |
1599 """ |
1650 """ |
1600 patternFilterObjects = None |
1651 patternFilterObjects = None |
1601 if filterString.strip(): |
1652 if filterString.strip(): |
1602 pattern = filterString.replace(';', '|') |
1653 pattern = filterString.replace(";", "|") |
1603 with contextlib.suppress(re.error): |
1654 with contextlib.suppress(re.error): |
1604 patternFilterObjects = re.compile(pattern) |
1655 patternFilterObjects = re.compile(pattern) |
1605 |
1656 |
1606 if scope: |
1657 if scope: |
1607 self.globalsFilterObjects = patternFilterObjects |
1658 self.globalsFilterObjects = patternFilterObjects |
1608 else: |
1659 else: |
1609 self.localsFilterObjects = patternFilterObjects |
1660 self.localsFilterObjects = patternFilterObjects |
1610 |
1661 |
1611 def __completionList(self, text): |
1662 def __completionList(self, text): |
1612 """ |
1663 """ |
1613 Private slot to handle the request for a commandline completion list. |
1664 Private slot to handle the request for a commandline completion list. |
1614 |
1665 |
1615 @param text the text to be completed (string) |
1666 @param text the text to be completed (string) |
1616 """ |
1667 """ |
1617 completerDelims = ' \t\n`~!@#$%^&*()-=+[{]}\\|;:\'",<>/?' |
1668 completerDelims = " \t\n`~!@#$%^&*()-=+[{]}\\|;:'\",<>/?" |
1618 |
1669 |
1619 completions = set() |
1670 completions = set() |
1620 # find position of last delim character |
1671 # find position of last delim character |
1621 pos = -1 |
1672 pos = -1 |
1622 while pos >= -len(text): |
1673 while pos >= -len(text): |
1623 if text[pos] in completerDelims: |
1674 if text[pos] in completerDelims: |
1624 if pos == -1: |
1675 if pos == -1: |
1625 text = '' |
1676 text = "" |
1626 else: |
1677 else: |
1627 text = text[pos + 1:] |
1678 text = text[pos + 1 :] |
1628 break |
1679 break |
1629 pos -= 1 |
1680 pos -= 1 |
1630 |
1681 |
1631 # Get local and global completions |
1682 # Get local and global completions |
1632 with contextlib.suppress(AttributeError): |
1683 with contextlib.suppress(AttributeError): |
1633 localdict = self.currentThread.getFrameLocals(self.framenr) |
1684 localdict = self.currentThread.getFrameLocals(self.framenr) |
1634 localCompleter = Completer(localdict).complete |
1685 localCompleter = Completer(localdict).complete |
1635 self.__getCompletionList(text, localCompleter, completions) |
1686 self.__getCompletionList(text, localCompleter, completions) |
1636 |
1687 |
1637 cf = self.currentThread.getCurrentFrame() |
1688 cf = self.currentThread.getCurrentFrame() |
1638 frmnr = self.framenr |
1689 frmnr = self.framenr |
1639 while cf is not None and frmnr > 0: |
1690 while cf is not None and frmnr > 0: |
1640 cf = cf.f_back |
1691 cf = cf.f_back |
1641 frmnr -= 1 |
1692 frmnr -= 1 |
1642 |
1693 |
1643 globaldict = self.debugMod.__dict__ if cf is None else cf.f_globals |
1694 globaldict = self.debugMod.__dict__ if cf is None else cf.f_globals |
1644 |
1695 |
1645 globalCompleter = Completer(globaldict).complete |
1696 globalCompleter = Completer(globaldict).complete |
1646 self.__getCompletionList(text, globalCompleter, completions) |
1697 self.__getCompletionList(text, globalCompleter, completions) |
1647 |
1698 |
1648 self.sendJsonCommand("ResponseCompletion", { |
1699 self.sendJsonCommand( |
1649 "completions": list(completions), |
1700 "ResponseCompletion", |
1650 "text": text, |
1701 { |
1651 }) |
1702 "completions": list(completions), |
|
1703 "text": text, |
|
1704 }, |
|
1705 ) |
1652 |
1706 |
1653 def __getCompletionList(self, text, completer, completions): |
1707 def __getCompletionList(self, text, completer, completions): |
1654 """ |
1708 """ |
1655 Private method to create a completions list. |
1709 Private method to create a completions list. |
1656 |
1710 |
1657 @param text text to complete (string) |
1711 @param text text to complete (string) |
1658 @param completer completer method |
1712 @param completer completer method |
1659 @param completions set where to add new completions strings (set) |
1713 @param completions set where to add new completions strings (set) |
1660 """ |
1714 """ |
1661 state = 0 |
1715 state = 0 |
1780 else: |
1852 else: |
1781 name = "debug_client_code" |
1853 name = "debug_client_code" |
1782 else: |
1854 else: |
1783 name = "debug_client_code" |
1855 name = "debug_client_code" |
1784 self.connectDebugger(port, remoteAddress, redirect, name=name) |
1856 self.connectDebugger(port, remoteAddress, redirect, name=name) |
1785 |
1857 |
1786 self._fncache = {} |
1858 self._fncache = {} |
1787 self.dircache = [] |
1859 self.dircache = [] |
1788 if codeStr: |
1860 if codeStr: |
1789 self.running = "<string>" |
1861 self.running = "<string>" |
1790 sys.argv = ["<string>"] + progargs[:] |
1862 sys.argv = ["<string>"] + progargs[:] |
1791 else: |
1863 else: |
1792 sys.argv = progargs[:] |
1864 sys.argv = progargs[:] |
1793 sys.argv[0] = os.path.abspath(sys.argv[0]) |
1865 sys.argv[0] = os.path.abspath(sys.argv[0]) |
1794 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
1866 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) |
1795 if wd == '': |
1867 if wd == "": |
1796 os.chdir(sys.path[1]) |
1868 os.chdir(sys.path[1]) |
1797 else: |
1869 else: |
1798 os.chdir(wd) |
1870 os.chdir(wd) |
1799 self.running = sys.argv[0] |
1871 self.running = sys.argv[0] |
1800 self.__setCoding(self.running) |
1872 self.__setCoding(self.running) |
1801 self.debugging = True |
1873 self.debugging = True |
1802 self.multiprocessSupport = multiprocessSupport |
1874 self.multiprocessSupport = multiprocessSupport |
1803 |
1875 |
1804 self.passive = passive |
1876 self.passive = passive |
1805 if passive: |
1877 if passive: |
1806 self.sendPassiveStartup(self.running, exceptions) |
1878 self.sendPassiveStartup(self.running, exceptions) |
1807 |
1879 |
1808 self.attachThread(mainThread=True) |
1880 self.attachThread(mainThread=True) |
1809 self.mainThread.tracePythonLibs(tracePython) |
1881 self.mainThread.tracePythonLibs(tracePython) |
1810 |
1882 |
1811 # set the system exception handling function to ensure, that |
1883 # set the system exception handling function to ensure, that |
1812 # we report on all unhandled exceptions |
1884 # we report on all unhandled exceptions |
1813 sys.excepthook = self.__unhandled_exception |
1885 sys.excepthook = self.__unhandled_exception |
1814 self.__interceptSignals() |
1886 self.__interceptSignals() |
1815 |
1887 |
1816 # This will eventually enter a local event loop. |
1888 # This will eventually enter a local event loop. |
1817 self.debugMod.__dict__['__file__'] = self.running |
1889 self.debugMod.__dict__["__file__"] = self.running |
1818 sys.modules['__main__'] = self.debugMod |
1890 sys.modules["__main__"] = self.debugMod |
1819 if codeStr: |
1891 if codeStr: |
1820 code = self.__compileCommand(codeStr) |
1892 code = self.__compileCommand(codeStr) |
1821 elif scriptModule: |
1893 elif scriptModule: |
1822 import runpy |
1894 import runpy |
|
1895 |
1823 modName, modSpec, code = runpy._get_module_details(scriptModule) |
1896 modName, modSpec, code = runpy._get_module_details(scriptModule) |
1824 self.running = code.co_filename |
1897 self.running = code.co_filename |
1825 self.debugMod.__dict__.clear() |
1898 self.debugMod.__dict__.clear() |
1826 self.debugMod.__dict__.update({ |
1899 self.debugMod.__dict__.update( |
1827 "__name__": "__main__", |
1900 { |
1828 "__file__": self.running, |
1901 "__name__": "__main__", |
1829 "__package__": modSpec.parent, |
1902 "__file__": self.running, |
1830 "__loader__": modSpec.loader, |
1903 "__package__": modSpec.parent, |
1831 "__spec__": modSpec, |
1904 "__loader__": modSpec.loader, |
1832 "__builtins__": __builtins__, |
1905 "__spec__": modSpec, |
1833 }) |
1906 "__builtins__": __builtins__, |
|
1907 } |
|
1908 ) |
1834 else: |
1909 else: |
1835 code = self.__compileFileSource(self.running) |
1910 code = self.__compileFileSource(self.running) |
1836 res = ( |
1911 res = ( |
1837 self.mainThread.run(code, self.debugMod.__dict__, debug=True) |
1912 self.mainThread.run(code, self.debugMod.__dict__, debug=True) |
1838 if code else |
1913 if code |
1839 42 # should not happen |
1914 else 42 # should not happen |
1840 ) |
1915 ) |
1841 return res |
1916 return res |
1842 |
1917 |
1843 def run_call(self, scriptname, func, *args): |
1918 def run_call(self, scriptname, func, *args): |
1844 """ |
1919 """ |
1845 Public method used to start the remote debugger and call a function. |
1920 Public method used to start the remote debugger and call a function. |
1846 |
1921 |
1847 @param scriptname name of the script to be debugged (string) |
1922 @param scriptname name of the script to be debugged (string) |
1848 @param func function to be called |
1923 @param func function to be called |
1849 @param *args arguments being passed to func |
1924 @param *args arguments being passed to func |
1850 @return result of the function call |
1925 @return result of the function call |
1851 """ |
1926 """ |
1852 self.startDebugger(scriptname, enableTrace=False) |
1927 self.startDebugger(scriptname, enableTrace=False) |
1853 res = self.mainThread.runcall(func, *args) |
1928 res = self.mainThread.runcall(func, *args) |
1854 self.progTerminated(res, closeSession=False) |
1929 self.progTerminated(res, closeSession=False) |
1855 return res |
1930 return res |
1856 |
1931 |
1857 def __resolveHost(self, host): |
1932 def __resolveHost(self, host): |
1858 """ |
1933 """ |
1859 Private method to resolve a hostname to an IP address. |
1934 Private method to resolve a hostname to an IP address. |
1860 |
1935 |
1861 @param host hostname of the debug server (string) |
1936 @param host hostname of the debug server (string) |
1862 @return IP address (string) |
1937 @return IP address (string) |
1863 """ |
1938 """ |
1864 try: |
1939 try: |
1865 host, version = host.split("@@") |
1940 host, version = host.split("@@") |
1866 except ValueError: |
1941 except ValueError: |
1867 version = 'v4' |
1942 version = "v4" |
1868 |
1943 |
1869 family = ( |
1944 family = ( |
1870 0 |
1945 0 |
1871 if version.startswith("i") else |
1946 if version.startswith("i") |
1872 (socket.AF_INET if version == 'v4' else socket.AF_INET6) |
1947 else (socket.AF_INET if version == "v4" else socket.AF_INET6) |
1873 ) |
1948 ) |
1874 |
1949 |
1875 with contextlib.suppress(OSError): |
1950 with contextlib.suppress(OSError): |
1876 addrinfo = socket.getaddrinfo( |
1951 addrinfo = socket.getaddrinfo(host, None, family, socket.SOCK_STREAM) |
1877 host, None, family, socket.SOCK_STREAM) |
|
1878 return addrinfo[0][4][0] |
1952 return addrinfo[0][4][0] |
1879 |
1953 |
1880 return None |
1954 return None |
1881 |
1955 |
1882 def main(self): |
1956 def main(self): |
1883 """ |
1957 """ |
1884 Public method implementing the main method. |
1958 Public method implementing the main method. |
1885 """ |
1959 """ |
1886 if '--' in sys.argv: |
1960 if "--" in sys.argv: |
1887 args = sys.argv[1:] |
1961 args = sys.argv[1:] |
1888 host = None |
1962 host = None |
1889 port = None |
1963 port = None |
1890 wd = '' |
1964 wd = "" |
1891 tracePython = False |
1965 tracePython = False |
1892 exceptions = True |
1966 exceptions = True |
1893 redirect = True |
1967 redirect = True |
1894 passive = True |
1968 passive = True |
1895 multiprocess = False |
1969 multiprocess = False |
1896 codeStr = "" |
1970 codeStr = "" |
1897 scriptModule = "" |
1971 scriptModule = "" |
1898 while args[0]: |
1972 while args[0]: |
1899 if args[0] == '-h': |
1973 if args[0] == "-h": |
1900 host = args[1] |
1974 host = args[1] |
1901 del args[0] |
1975 del args[0] |
1902 del args[0] |
1976 del args[0] |
1903 elif args[0] == '-p': |
1977 elif args[0] == "-p": |
1904 port = int(args[1]) |
1978 port = int(args[1]) |
1905 del args[0] |
1979 del args[0] |
1906 del args[0] |
1980 del args[0] |
1907 elif args[0] == '-w': |
1981 elif args[0] == "-w": |
1908 wd = args[1] |
1982 wd = args[1] |
1909 del args[0] |
1983 del args[0] |
1910 del args[0] |
1984 del args[0] |
1911 elif args[0] == '-t': |
1985 elif args[0] == "-t": |
1912 tracePython = True |
1986 tracePython = True |
1913 del args[0] |
1987 del args[0] |
1914 elif args[0] == '-e': |
1988 elif args[0] == "-e": |
1915 exceptions = False |
1989 exceptions = False |
1916 del args[0] |
1990 del args[0] |
1917 elif args[0] == '-n': |
1991 elif args[0] == "-n": |
1918 redirect = False |
1992 redirect = False |
1919 del args[0] |
1993 del args[0] |
1920 elif args[0] == '--no-encoding': |
1994 elif args[0] == "--no-encoding": |
1921 self.noencoding = True |
1995 self.noencoding = True |
1922 del args[0] |
1996 del args[0] |
1923 elif args[0] == '--no-passive': |
1997 elif args[0] == "--no-passive": |
1924 passive = False |
1998 passive = False |
1925 del args[0] |
1999 del args[0] |
1926 elif args[0] == '--multiprocess': |
2000 elif args[0] == "--multiprocess": |
1927 multiprocess = True |
2001 multiprocess = True |
1928 del args[0] |
2002 del args[0] |
1929 elif args[0] in ('-c', '--code'): |
2003 elif args[0] in ("-c", "--code"): |
1930 codeStr = args[1] |
2004 codeStr = args[1] |
1931 del args[0] |
2005 del args[0] |
1932 del args[0] |
2006 del args[0] |
1933 elif args[0] in ('-m', '--module'): |
2007 elif args[0] in ("-m", "--module"): |
1934 scriptModule = args[1] |
2008 scriptModule = args[1] |
1935 del args[0] |
2009 del args[0] |
1936 del args[0] |
2010 del args[0] |
1937 elif args[0] == '--': |
2011 elif args[0] == "--": |
1938 del args[0] |
2012 del args[0] |
1939 break |
2013 break |
1940 else: # unknown option |
2014 else: # unknown option |
1941 del args[0] |
2015 del args[0] |
1942 if not args: |
2016 if not args: |
1943 print("No program given. Aborting!") |
2017 print("No program given. Aborting!") |
1944 # __IGNORE_WARNING_M801__ |
2018 # __IGNORE_WARNING_M801__ |
1945 elif "-m" in args: |
2019 elif "-m" in args: |
1946 print("Running module as a script is not supported. Aborting!") |
2020 print("Running module as a script is not supported. Aborting!") |
1947 # __IGNORE_WARNING_M801__ |
2021 # __IGNORE_WARNING_M801__ |
1948 else: |
2022 else: |
1949 # Store options in case a new Python process is created |
2023 # Store options in case a new Python process is created |
1950 self.startOptions = ( |
2024 self.startOptions = ( |
1951 wd, host, port, exceptions, tracePython, redirect, |
2025 wd, |
|
2026 host, |
|
2027 port, |
|
2028 exceptions, |
|
2029 tracePython, |
|
2030 redirect, |
1952 self.noencoding, |
2031 self.noencoding, |
1953 ) |
2032 ) |
1954 if not self.noencoding: |
2033 if not self.noencoding: |
1955 self.__coding = self.defaultCoding |
2034 self.__coding = self.defaultCoding |
1956 patchNewProcessFunctions(multiprocess, self) |
2035 patchNewProcessFunctions(multiprocess, self) |
1957 res = self.startProgInDebugger( |
2036 res = self.startProgInDebugger( |
1958 args, wd, host, port, exceptions=exceptions, |
2037 args, |
1959 tracePython=tracePython, redirect=redirect, |
2038 wd, |
1960 passive=passive, multiprocessSupport=multiprocess, |
2039 host, |
1961 codeStr=codeStr, scriptModule=scriptModule, |
2040 port, |
|
2041 exceptions=exceptions, |
|
2042 tracePython=tracePython, |
|
2043 redirect=redirect, |
|
2044 passive=passive, |
|
2045 multiprocessSupport=multiprocess, |
|
2046 codeStr=codeStr, |
|
2047 scriptModule=scriptModule, |
1962 ) |
2048 ) |
1963 sys.exit(res) |
2049 sys.exit(res) |
1964 else: |
2050 else: |
1965 if sys.argv[1] == '--no-encoding': |
2051 if sys.argv[1] == "--no-encoding": |
1966 self.noencoding = True |
2052 self.noencoding = True |
1967 del sys.argv[1] |
2053 del sys.argv[1] |
1968 |
2054 |
1969 if sys.argv[1] == '--multiprocess': |
2055 if sys.argv[1] == "--multiprocess": |
1970 self.multiprocessSupport = True |
2056 self.multiprocessSupport = True |
1971 del sys.argv[1] |
2057 del sys.argv[1] |
1972 |
2058 |
1973 if sys.argv[1] == '': |
2059 if sys.argv[1] == "": |
1974 del sys.argv[1] |
2060 del sys.argv[1] |
1975 |
2061 |
1976 try: |
2062 try: |
1977 port = int(sys.argv[1]) |
2063 port = int(sys.argv[1]) |
1978 except (ValueError, IndexError): |
2064 except (ValueError, IndexError): |
1979 port = -1 |
2065 port = -1 |
1980 |
2066 |
1981 if sys.argv[2] == "True": |
2067 if sys.argv[2] == "True": |
1982 redirect = True |
2068 redirect = True |
1983 elif sys.argv[2] == "False": |
2069 elif sys.argv[2] == "False": |
1984 redirect = False |
2070 redirect = False |
1985 else: |
2071 else: |
1986 try: |
2072 try: |
1987 redirect = int(sys.argv[2]) |
2073 redirect = int(sys.argv[2]) |
1988 except (ValueError, IndexError): |
2074 except (ValueError, IndexError): |
1989 redirect = True |
2075 redirect = True |
1990 |
2076 |
1991 ipOrHost = sys.argv[3] |
2077 ipOrHost = sys.argv[3] |
1992 if ':' in ipOrHost or ipOrHost[0] in '0123456789': |
2078 if ":" in ipOrHost or ipOrHost[0] in "0123456789": |
1993 # IPv6 address or IPv4 address |
2079 # IPv6 address or IPv4 address |
1994 remoteAddress = ipOrHost |
2080 remoteAddress = ipOrHost |
1995 else: |
2081 else: |
1996 remoteAddress = self.__resolveHost(ipOrHost) |
2082 remoteAddress = self.__resolveHost(ipOrHost) |
1997 |
2083 |
1998 sys.argv = [''] |
2084 sys.argv = [""] |
1999 if '' not in sys.path: |
2085 if "" not in sys.path: |
2000 sys.path.insert(0, '') |
2086 sys.path.insert(0, "") |
2001 |
2087 |
2002 if port >= 0: |
2088 if port >= 0: |
2003 # Store options in case a new Python process is created |
2089 # Store options in case a new Python process is created |
2004 self.startOptions = ( |
2090 self.startOptions = ( |
2005 '', remoteAddress, port, True, False, redirect, |
2091 "", |
|
2092 remoteAddress, |
|
2093 port, |
|
2094 True, |
|
2095 False, |
|
2096 redirect, |
2006 self.noencoding, |
2097 self.noencoding, |
2007 ) |
2098 ) |
2008 if not self.noencoding: |
2099 if not self.noencoding: |
2009 self.__coding = self.defaultCoding |
2100 self.__coding = self.defaultCoding |
2010 patchNewProcessFunctions(self.multiprocessSupport, self) |
2101 patchNewProcessFunctions(self.multiprocessSupport, self) |
2011 self.connectDebugger(port, remoteAddress, redirect) |
2102 self.connectDebugger(port, remoteAddress, redirect) |
2012 self.__interact() |
2103 self.__interact() |
2013 else: |
2104 else: |
2014 print("No network port given. Aborting...") |
2105 print("No network port given. Aborting...") |
2015 # __IGNORE_WARNING_M801__ |
2106 # __IGNORE_WARNING_M801__ |
2016 |
2107 |
2017 def close(self, fd): |
2108 def close(self, fd): |
2018 """ |
2109 """ |
2019 Public method implementing a close method as a replacement for |
2110 Public method implementing a close method as a replacement for |
2020 os.close(). |
2111 os.close(). |
2021 |
2112 |
2022 It prevents the debugger connections from being closed. |
2113 It prevents the debugger connections from being closed. |
2023 |
2114 |
2024 @param fd file descriptor to be closed (integer) |
2115 @param fd file descriptor to be closed (integer) |
2025 """ |
2116 """ |
2026 if fd in [self.readstream.fileno(), self.writestream.fileno(), |
2117 if fd in [ |
2027 self.errorstream.fileno()]: |
2118 self.readstream.fileno(), |
|
2119 self.writestream.fileno(), |
|
2120 self.errorstream.fileno(), |
|
2121 ]: |
2028 return |
2122 return |
2029 |
2123 |
2030 DebugClientOrigClose(fd) |
2124 DebugClientOrigClose(fd) |
2031 |
2125 |
2032 def __getSysPath(self, firstEntry): |
2126 def __getSysPath(self, firstEntry): |
2033 """ |
2127 """ |
2034 Private slot to calculate a path list including the PYTHONPATH |
2128 Private slot to calculate a path list including the PYTHONPATH |
2035 environment variable. |
2129 environment variable. |
2036 |
2130 |
2037 @param firstEntry entry to be put first in sys.path (string) |
2131 @param firstEntry entry to be put first in sys.path (string) |
2038 @return path list for use as sys.path (list of strings) |
2132 @return path list for use as sys.path (list of strings) |
2039 """ |
2133 """ |
2040 sysPath = [path for path in os.environ.get("PYTHONPATH", "") |
2134 sysPath = [ |
2041 .split(os.pathsep) |
2135 path |
2042 if path not in sys.path] + sys.path[:] |
2136 for path in os.environ.get("PYTHONPATH", "").split(os.pathsep) |
|
2137 if path not in sys.path |
|
2138 ] + sys.path[:] |
2043 if "" in sysPath: |
2139 if "" in sysPath: |
2044 sysPath.remove("") |
2140 sysPath.remove("") |
2045 sysPath.insert(0, firstEntry) |
2141 sysPath.insert(0, firstEntry) |
2046 sysPath.insert(0, '') |
2142 sysPath.insert(0, "") |
2047 return sysPath |
2143 return sysPath |
2048 |
2144 |
2049 def skipMultiProcessDebugging(self, scriptName): |
2145 def skipMultiProcessDebugging(self, scriptName): |
2050 """ |
2146 """ |
2051 Public method to check, if the given script is eligible for debugging. |
2147 Public method to check, if the given script is eligible for debugging. |
2052 |
2148 |
2053 @param scriptName name of the script to check |
2149 @param scriptName name of the script to check |
2054 @type str |
2150 @type str |
2055 @return flag indicating eligibility |
2151 @return flag indicating eligibility |
2056 @rtype bool |
2152 @rtype bool |
2057 """ |
2153 """ |
2058 return any(fnmatch.fnmatch(scriptName, pattern) |
2154 return any(fnmatch.fnmatch(scriptName, pattern) for pattern in self.noDebugList) |
2059 for pattern in self.noDebugList) |
|