src/eric7/DebugClients/Python/DebugClientBase.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9383
1d9a71952123
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
22 import traceback 22 import traceback
23 import types 23 import types
24 24
25 import DebugClientCapabilities 25 import DebugClientCapabilities
26 import DebugVariables 26 import DebugVariables
27 from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__ 27 from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__
28 from AsyncFile import AsyncFile, AsyncPendingWrite 28 from AsyncFile import AsyncFile, AsyncPendingWrite
29 from DebugConfig import SpecialAttributes, NonExpandableTypes 29 from DebugConfig import SpecialAttributes, NonExpandableTypes
30 from FlexCompleter import Completer 30 from FlexCompleter import Completer
31 from DebugUtilities import prepareJsonCommand 31 from DebugUtilities import prepareJsonCommand
32 from BreakpointWatch import Breakpoint, Watch 32 from BreakpointWatch import Breakpoint, Watch
40 40
41 41
42 def DebugClientInput(prompt="", echo=True): 42 def DebugClientInput(prompt="", echo=True):
43 """ 43 """
44 Replacement for the standard input() builtin. 44 Replacement for the standard input() builtin.
45 45
46 This function works with the split debugger. 46 This function works with the split debugger.
47 47
48 @param prompt prompt to be shown 48 @param prompt prompt to be shown
49 @type str 49 @type str
50 @param echo flag indicating echoing of the input 50 @param echo flag indicating echoing of the input
51 @type bool 51 @type bool
52 @return result of the input() call 52 @return result of the input() call
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)
578 res = exc.code 599 res = exc.code
579 atexit._run_exitfuncs() 600 atexit._run_exitfuncs()
580 except Exception: 601 except Exception:
581 excinfo = sys.exc_info() 602 excinfo = sys.exc_info()
582 self.__unhandled_exception(*excinfo) 603 self.__unhandled_exception(*excinfo)
583 604
584 self.prof.save() 605 self.prof.save()
585 self.progTerminated(res, closeSession=False) 606 self.progTerminated(res, closeSession=False)
586 607
587 elif method == "ExecuteStatement": 608 elif method == "ExecuteStatement":
588 if self.buffer: 609 if self.buffer:
589 self.buffer = self.buffer + '\n' + params["statement"] 610 self.buffer = self.buffer + "\n" + params["statement"]
590 else: 611 else:
591 self.buffer = params["statement"] 612 self.buffer = params["statement"]
592 613
593 try: 614 try:
594 code = self.compile_command(self.buffer, self.readstream.name) 615 code = self.compile_command(self.buffer, self.readstream.name)
595 except (OverflowError, SyntaxError, ValueError): 616 except (OverflowError, SyntaxError, ValueError):
596 # Report the exception 617 # Report the exception
597 sys.last_type, sys.last_value, sys.last_traceback = ( 618 sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
598 sys.exc_info()) 619 self.sendJsonCommand(
599 self.sendJsonCommand("ClientOutput", { 620 "ClientOutput",
600 "text": "".join(traceback.format_exception_only( 621 {
601 sys.last_type, sys.last_value)) 622 "text": "".join(
602 }) 623 traceback.format_exception_only(
603 self.buffer = '' 624 sys.last_type, sys.last_value
625 )
626 )
627 },
628 )
629 self.buffer = ""
604 else: 630 else:
605 if code is None: 631 if code is None:
606 self.sendJsonCommand("ResponseContinue", {}) 632 self.sendJsonCommand("ResponseContinue", {})
607 return 633 return
608 else: 634 else:
609 self.buffer = '' 635 self.buffer = ""
610 636
611 try: 637 try:
612 if self.running is None: 638 if self.running is None:
613 exec(code, self.debugMod.__dict__) # secok 639 exec(code, self.debugMod.__dict__) # secok
614 else: 640 else:
615 if self.currentThread is None: 641 if self.currentThread is None:
616 # program has terminated 642 # program has terminated
617 self.running = None 643 self.running = None
618 _globals = self.debugMod.__dict__ 644 _globals = self.debugMod.__dict__
628 frmnr = self.framenr 654 frmnr = self.framenr
629 while cf is not None and frmnr > 0: 655 while cf is not None and frmnr > 0:
630 cf = cf.f_back 656 cf = cf.f_back
631 frmnr -= 1 657 frmnr -= 1
632 _globals = cf.f_globals 658 _globals = cf.f_globals
633 _locals = ( 659 _locals = self.currentThread.getFrameLocals(
634 self.currentThread.getFrameLocals( 660 self.framenr
635 self.framenr)) 661 )
636 # transfer all locals into a new globals 662 # transfer all locals into a new globals
637 # to emulate Python scoping rules 663 # to emulate Python scoping rules
638 _updatedGlobals = {} 664 _updatedGlobals = {}
639 _updatedGlobals.update(_globals) 665 _updatedGlobals.update(_globals)
640 _updatedGlobals.update(_locals) 666 _updatedGlobals.update(_locals)
641 #- reset sys.stdout to our redirector 667 # - reset sys.stdout to our redirector
642 #- (unconditionally) 668 # - (unconditionally)
643 if "sys" in _globals: 669 if "sys" in _globals:
644 __stdout = _updatedGlobals["sys"].stdout 670 __stdout = _updatedGlobals["sys"].stdout
645 _updatedGlobals["sys"].stdout = ( 671 _updatedGlobals["sys"].stdout = self.writestream
646 self.writestream 672 exec(code, _updatedGlobals, _locals) # secok
647 )
648 exec(code, _updatedGlobals, _locals) # secok
649 _updatedGlobals["sys"].stdout = __stdout 673 _updatedGlobals["sys"].stdout = __stdout
650 elif "sys" in _locals: 674 elif "sys" in _locals:
651 __stdout = _locals["sys"].stdout 675 __stdout = _locals["sys"].stdout
652 _locals["sys"].stdout = self.writestream 676 _locals["sys"].stdout = self.writestream
653 exec(code, _updatedGlobals, _locals) # secok 677 exec(code, _updatedGlobals, _locals) # secok
654 _locals["sys"].stdout = __stdout 678 _locals["sys"].stdout = __stdout
655 else: 679 else:
656 exec(code, _updatedGlobals, _locals) # secok 680 exec(code, _updatedGlobals, _locals) # secok
657 681
658 self.currentThread.storeFrameLocals(self.framenr) 682 self.currentThread.storeFrameLocals(self.framenr)
659 except SystemExit as exc: 683 except SystemExit as exc:
660 self.progTerminated(exc.code) 684 self.progTerminated(exc.code)
661 except Exception: 685 except Exception:
662 # Report the exception and the traceback 686 # Report the exception and the traceback
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
991 1032
992 if error or len(command) == 0: 1033 if error or len(command) == 0:
993 self.sessionClose() 1034 self.sessionClose()
994 else: 1035 else:
995 self.handleJsonCommand(command) 1036 self.handleJsonCommand(command)
996 1037
997 return error 1038 return error
998 1039
999 def writeReady(self, stream): 1040 def writeReady(self, stream):
1000 """ 1041 """
1001 Public method called when we are ready to write data. 1042 Public method called when we are ready to write data.
1002 1043
1003 @param stream file like object that has data to be written 1044 @param stream file like object that has data to be written
1004 """ 1045 """
1005 stream.write_p("") 1046 stream.write_p("")
1006 stream.flush() 1047 stream.flush()
1007 1048
1008 def __interact(self): 1049 def __interact(self):
1009 """ 1050 """
1010 Private method to interact with the debugger. 1051 Private method to interact with the debugger.
1011 """ 1052 """
1012 global DebugClientInstance 1053 global DebugClientInstance
1019 self.eventLoop() 1060 self.eventLoop()
1020 1061
1021 def eventLoop(self, disablePolling=False): 1062 def eventLoop(self, disablePolling=False):
1022 """ 1063 """
1023 Public method implementing our event loop. 1064 Public method implementing our event loop.
1024 1065
1025 @param disablePolling flag indicating to enter an event loop with 1066 @param disablePolling flag indicating to enter an event loop with
1026 polling disabled (boolean) 1067 polling disabled (boolean)
1027 """ 1068 """
1028 self.eventExit = False 1069 self.eventExit = False
1029 self.pollingDisabled = disablePolling 1070 self.pollingDisabled = disablePolling
1032 while not self.eventExit: 1073 while not self.eventExit:
1033 wrdy = [] 1074 wrdy = []
1034 1075
1035 if self.writestream.nWriteErrors > self.writestream.MAX_TRIES: 1076 if self.writestream.nWriteErrors > self.writestream.MAX_TRIES:
1036 break 1077 break
1037 1078
1038 if AsyncPendingWrite(self.writestream): 1079 if AsyncPendingWrite(self.writestream):
1039 wrdy.append(self.writestream) 1080 wrdy.append(self.writestream)
1040 1081
1041 if AsyncPendingWrite(self.errorstream): 1082 if AsyncPendingWrite(self.errorstream):
1042 wrdy.append(self.errorstream) 1083 wrdy.append(self.errorstream)
1043 1084
1044 try: 1085 try:
1045 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, []) 1086 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [])
1046 except (KeyboardInterrupt, OSError): 1087 except (KeyboardInterrupt, OSError):
1047 selectErrors += 1 1088 selectErrors += 1
1048 if selectErrors <= 10: # arbitrarily selected 1089 if selectErrors <= 10: # arbitrarily selected
1049 # just carry on 1090 # just carry on
1050 continue 1091 continue
1051 else: 1092 else:
1052 # give up for too many errors 1093 # give up for too many errors
1053 break 1094 break
1054 except ValueError: 1095 except ValueError:
1055 # the client socket might already be closed, i.e. its fd is -1 1096 # the client socket might already be closed, i.e. its fd is -1
1056 break 1097 break
1057 1098
1058 # reset the select error counter 1099 # reset the select error counter
1059 selectErrors = 0 1100 selectErrors = 0
1060 1101
1061 if self.readstream in rrdy: 1102 if self.readstream in rrdy:
1062 error = self.readReady(self.readstream) 1103 error = self.readReady(self.readstream)
1063 if error: 1104 if error:
1064 break 1105 break
1065 1106
1076 """ 1117 """
1077 Public method to poll for events like 'set break point'. 1118 Public method to poll for events like 'set break point'.
1078 """ 1119 """
1079 if self.pollingDisabled: 1120 if self.pollingDisabled:
1080 return 1121 return
1081 1122
1082 wrdy = [] 1123 wrdy = []
1083 if AsyncPendingWrite(self.writestream): 1124 if AsyncPendingWrite(self.writestream):
1084 wrdy.append(self.writestream) 1125 wrdy.append(self.writestream)
1085 1126
1086 if AsyncPendingWrite(self.errorstream): 1127 if AsyncPendingWrite(self.errorstream):
1087 wrdy.append(self.errorstream) 1128 wrdy.append(self.errorstream)
1088 1129
1089 # immediate return if nothing is ready. 1130 # immediate return if nothing is ready.
1090 try: 1131 try:
1091 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [], 0) 1132 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [], 0)
1092 except (KeyboardInterrupt, OSError): 1133 except (KeyboardInterrupt, OSError):
1093 return 1134 return
1098 if self.writestream in wrdy: 1139 if self.writestream in wrdy:
1099 self.writeReady(self.writestream) 1140 self.writeReady(self.writestream)
1100 1141
1101 if self.errorstream in wrdy: 1142 if self.errorstream in wrdy:
1102 self.writeReady(self.errorstream) 1143 self.writeReady(self.errorstream)
1103 1144
1104 def connectDebugger(self, port, remoteAddress=None, redirect=True, 1145 def connectDebugger(self, port, remoteAddress=None, redirect=True, name=""):
1105 name=""):
1106 """ 1146 """
1107 Public method to establish a session with the debugger. 1147 Public method to establish a session with the debugger.
1108 1148
1109 It opens a network connection to the debugger, connects it to stdin, 1149 It opens a network connection to the debugger, connects it to stdin,
1110 stdout and stderr and saves these file objects in case the application 1150 stdout and stderr and saves these file objects in case the application
1111 being debugged redirects them itself. 1151 being debugged redirects them itself.
1112 1152
1113 @param port the port number to connect to 1153 @param port the port number to connect to
1114 @type int 1154 @type int
1115 @param remoteAddress the network address of the debug server host 1155 @param remoteAddress the network address of the debug server host
1116 @type str 1156 @type str
1117 @param redirect flag indicating redirection of stdin, stdout and 1157 @param redirect flag indicating redirection of stdin, stdout and
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 """
1191 message = "Illegal Instruction" 1231 message = "Illegal Instruction"
1192 elif signalNumber == signal.SIGSEGV: 1232 elif signalNumber == signal.SIGSEGV:
1193 message = "Segmentation Violation" 1233 message = "Segmentation Violation"
1194 else: 1234 else:
1195 message = "Unknown Signal '{0}'".format(signalNumber) 1235 message = "Unknown Signal '{0}'".format(signalNumber)
1196 1236
1197 filename = self.absPath(stackFrame) 1237 filename = self.absPath(stackFrame)
1198 1238
1199 linenr = stackFrame.f_lineno 1239 linenr = stackFrame.f_lineno
1200 ffunc = stackFrame.f_code.co_name 1240 ffunc = stackFrame.f_code.co_name
1201 1241
1202 if ffunc == '?': 1242 if ffunc == "?":
1203 ffunc = '' 1243 ffunc = ""
1204 1244
1205 if ffunc and not ffunc.startswith("<"): 1245 if ffunc and not ffunc.startswith("<"):
1206 argInfo = getargvalues(stackFrame) 1246 argInfo = getargvalues(stackFrame)
1207 try: 1247 try:
1208 fargs = formatargvalues( 1248 fargs = formatargvalues(
1209 argInfo.args, argInfo.varargs, 1249 argInfo.args, argInfo.varargs, argInfo.keywords, argInfo.locals
1210 argInfo.keywords, argInfo.locals) 1250 )
1211 except Exception: 1251 except Exception:
1212 fargs = "" 1252 fargs = ""
1213 else: 1253 else:
1214 fargs = "" 1254 fargs = ""
1215 1255
1216 self.sendJsonCommand("ResponseSignal", { 1256 self.sendJsonCommand(
1217 "message": message, 1257 "ResponseSignal",
1218 "filename": filename, 1258 {
1219 "linenumber": linenr, 1259 "message": message,
1220 "function": ffunc, 1260 "filename": filename,
1221 "arguments": fargs, 1261 "linenumber": linenr,
1222 }) 1262 "function": ffunc,
1223 1263 "arguments": fargs,
1264 },
1265 )
1266
1224 def absPath(self, fn): 1267 def absPath(self, fn):
1225 """ 1268 """
1226 Public method to convert a filename to an absolute name. 1269 Public method to convert a filename to an absolute name.
1227 1270
1228 sys.path is used as a set of possible prefixes. The name stays 1271 sys.path is used as a set of possible prefixes. The name stays
1229 relative if a file could not be found. 1272 relative if a file could not be found.
1230 1273
1231 @param fn filename (string) 1274 @param fn filename (string)
1232 @return the converted filename (string) 1275 @return the converted filename (string)
1233 """ 1276 """
1234 if os.path.isabs(fn): 1277 if os.path.isabs(fn):
1235 return fn 1278 return fn
1252 1295
1253 # Search the additional directory cache 1296 # Search the additional directory cache
1254 for p in self.dircache: 1297 for p in self.dircache:
1255 afn = os.path.abspath(os.path.join(p, fn)) 1298 afn = os.path.abspath(os.path.join(p, fn))
1256 nafn = os.path.normcase(afn) 1299 nafn = os.path.normcase(afn)
1257 1300
1258 if os.path.exists(nafn): 1301 if os.path.exists(nafn):
1259 self._fncache[fn] = afn 1302 self._fncache[fn] = afn
1260 return afn 1303 return afn
1261 1304
1262 # Nothing found. 1305 # Nothing found.
1263 return fn 1306 return fn
1264 1307
1265 def getRunning(self): 1308 def getRunning(self):
1266 """ 1309 """
1267 Public method to return the main script we are currently running. 1310 Public method to return the main script we are currently running.
1268 1311
1269 @return flag indicating a running debug session (boolean) 1312 @return flag indicating a running debug session (boolean)
1270 """ 1313 """
1271 return self.running 1314 return self.running
1272 1315
1273 def progTerminated(self, status, message="", closeSession=True): 1316 def progTerminated(self, status, message="", closeSession=True):
1274 """ 1317 """
1275 Public method to tell the debugger that the program has terminated. 1318 Public method to tell the debugger that the program has terminated.
1276 1319
1277 @param status return status 1320 @param status return status
1278 @type int 1321 @type int
1279 @param message status message 1322 @param message status message
1280 @type str 1323 @type str
1281 @param closeSession flag indicating to close the debugger session 1324 @param closeSession flag indicating to close the debugger session
1286 elif not isinstance(status, int): 1329 elif not isinstance(status, int):
1287 message = str(status) 1330 message = str(status)
1288 status = 1 1331 status = 1
1289 if message is None: 1332 if message is None:
1290 message = "" 1333 message = ""
1291 1334
1292 if self.running: 1335 if self.running:
1293 self.set_quit() 1336 self.set_quit()
1294 program = self.running 1337 program = self.running
1295 self.running = None 1338 self.running = None
1296 self.sendJsonCommand("ResponseExit", { 1339 self.sendJsonCommand(
1297 "status": status, 1340 "ResponseExit",
1298 "message": message, 1341 {
1299 "program": program, 1342 "status": status,
1300 }) 1343 "message": message,
1301 1344 "program": program,
1345 },
1346 )
1347
1302 # reset coding 1348 # reset coding
1303 self.__coding = self.defaultCoding 1349 self.__coding = self.defaultCoding
1304 1350
1305 if closeSession: 1351 if closeSession:
1306 self.sessionClose(False) 1352 self.sessionClose(False)
1307 1353
1308 def __dumpVariables(self, frmnr, scope, filterList): 1354 def __dumpVariables(self, frmnr, scope, filterList):
1309 """ 1355 """
1310 Private method to return the variables of a frame to the debug server. 1356 Private method to return the variables of a frame to the debug server.
1311 1357
1312 @param frmnr distance of frame reported on. 0 is the current frame 1358 @param frmnr distance of frame reported on. 0 is the current frame
1313 @type int 1359 @type int
1314 @param scope 1 to report global variables, 0 for local variables 1360 @param scope 1 to report global variables, 0 for local variables
1315 @type int 1361 @type int
1316 @param filterList list of variable types to be filtered 1362 @param filterList list of variable types to be filtered
1317 @type list of str 1363 @type list of str
1318 """ 1364 """
1319 if self.currentThread is None: 1365 if self.currentThread is None:
1320 return 1366 return
1321 1367
1322 self.resolverCache = [{}, {}] 1368 self.resolverCache = [{}, {}]
1323 frmnr += self.currentThread.skipFrames 1369 frmnr += self.currentThread.skipFrames
1324 if scope == 0: 1370 if scope == 0:
1325 self.framenr = frmnr 1371 self.framenr = frmnr
1326 1372
1327 f = self.currentThread.getCurrentFrame() 1373 f = self.currentThread.getCurrentFrame()
1328 1374
1329 while f is not None and frmnr > 0: 1375 while f is not None and frmnr > 0:
1330 f = f.f_back 1376 f = f.f_back
1331 frmnr -= 1 1377 frmnr -= 1
1332 1378
1333 if f is None: 1379 if f is None:
1334 if scope: 1380 if scope:
1335 varDict = self.debugMod.__dict__ 1381 varDict = self.debugMod.__dict__
1336 else: 1382 else:
1337 scope = -2 1383 scope = -2
1339 varDict = f.f_globals 1385 varDict = f.f_globals
1340 elif f.f_globals is f.f_locals: 1386 elif f.f_globals is f.f_locals:
1341 scope = -1 1387 scope = -1
1342 else: 1388 else:
1343 varDict = f.f_locals 1389 varDict = f.f_locals
1344 1390
1345 # Update known types list 1391 # Update known types list
1346 DebugVariables.updateTypeMap() 1392 DebugVariables.updateTypeMap()
1347 1393
1348 varlist = [] if scope < 0 else self.__formatVariablesList( 1394 varlist = (
1349 varDict.items(), scope, filterList) 1395 []
1350 1396 if scope < 0
1351 self.sendJsonCommand("ResponseVariables", { 1397 else self.__formatVariablesList(varDict.items(), scope, filterList)
1352 "scope": scope, 1398 )
1353 "variables": varlist, 1399
1354 }) 1400 self.sendJsonCommand(
1355 1401 "ResponseVariables",
1402 {
1403 "scope": scope,
1404 "variables": varlist,
1405 },
1406 )
1407
1356 def __dumpVariable(self, var, frmnr, scope, filterList): 1408 def __dumpVariable(self, var, frmnr, scope, filterList):
1357 """ 1409 """
1358 Private method to return the variables of a frame to the debug server. 1410 Private method to return the variables of a frame to the debug server.
1359 1411
1360 @param var list encoded name of the requested variable 1412 @param var list encoded name of the requested variable
1361 @type list of str 1413 @type list of str
1362 @param frmnr distance of frame reported on. 0 is the current frame 1414 @param frmnr distance of frame reported on. 0 is the current frame
1363 @type int 1415 @type int
1364 @param scope 1 to report global variables, 0 for local variables 1416 @param scope 1 to report global variables, 0 for local variables
1366 @param filterList list of variable types to be filtered 1418 @param filterList list of variable types to be filtered
1367 @type list of int 1419 @type list of int
1368 """ 1420 """
1369 if self.currentThread is None: 1421 if self.currentThread is None:
1370 return 1422 return
1371 1423
1372 frmnr += self.currentThread.skipFrames 1424 frmnr += self.currentThread.skipFrames
1373 f = self.currentThread.getCurrentFrame() 1425 f = self.currentThread.getCurrentFrame()
1374 1426
1375 while f is not None and frmnr > 0: 1427 while f is not None and frmnr > 0:
1376 f = f.f_back 1428 f = f.f_back
1377 frmnr -= 1 1429 frmnr -= 1
1378 1430
1379 if f is None: 1431 if f is None:
1380 if scope: 1432 if scope:
1381 varDict = self.debugMod.__dict__ 1433 varDict = self.debugMod.__dict__
1382 else: 1434 else:
1383 scope = -1 1435 scope = -1
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
1668 state += 1 1722 state += 1
1669 try: 1723 try:
1670 comp = completer(text, state) 1724 comp = completer(text, state)
1671 except Exception: 1725 except Exception:
1672 comp = None 1726 comp = None
1673 1727
1674 def startDebugger(self, filename=None, host=None, port=None, 1728 def startDebugger(
1675 enableTrace=True, exceptions=True, tracePython=False, 1729 self,
1676 redirect=True, passive=True, multiprocessSupport=False): 1730 filename=None,
1731 host=None,
1732 port=None,
1733 enableTrace=True,
1734 exceptions=True,
1735 tracePython=False,
1736 redirect=True,
1737 passive=True,
1738 multiprocessSupport=False,
1739 ):
1677 """ 1740 """
1678 Public method used to start the remote debugger. 1741 Public method used to start the remote debugger.
1679 1742
1680 @param filename the program to be debugged 1743 @param filename the program to be debugged
1681 @type str 1744 @type str
1682 @param host hostname of the debug server 1745 @param host hostname of the debug server
1683 @type str 1746 @type str
1684 @param port portnumber of the debug server 1747 @param port portnumber of the debug server
1697 @param multiprocessSupport flag indicating to enable multiprocess 1760 @param multiprocessSupport flag indicating to enable multiprocess
1698 debugging support 1761 debugging support
1699 @type bool 1762 @type bool
1700 """ 1763 """
1701 if host is None: 1764 if host is None:
1702 host = os.getenv('ERICHOST', 'localhost') 1765 host = os.getenv("ERICHOST", "localhost")
1703 if port is None: 1766 if port is None:
1704 port = os.getenv('ERICPORT', 42424) 1767 port = os.getenv("ERICPORT", 42424)
1705 1768
1706 remoteAddress = self.__resolveHost(host) 1769 remoteAddress = self.__resolveHost(host)
1707 name = os.path.basename(filename) if filename is not None else "" 1770 name = os.path.basename(filename) if filename is not None else ""
1708 self.connectDebugger(port, remoteAddress, redirect, name=name) 1771 self.connectDebugger(port, remoteAddress, redirect, name=name)
1709 if filename is not None: 1772 if filename is not None:
1710 self.running = os.path.abspath(filename) 1773 self.running = os.path.abspath(filename)
1715 self.running = None 1778 self.running = None
1716 if self.running: 1779 if self.running:
1717 self.__setCoding(self.running) 1780 self.__setCoding(self.running)
1718 self.passive = passive 1781 self.passive = passive
1719 self.__interact() 1782 self.__interact()
1720 1783
1721 # setup the debugger variables 1784 # setup the debugger variables
1722 self._fncache = {} 1785 self._fncache = {}
1723 self.dircache = [] 1786 self.dircache = []
1724 self.debugging = True 1787 self.debugging = True
1725 1788
1726 self.attachThread(mainThread=True) 1789 self.attachThread(mainThread=True)
1727 self.mainThread.tracePythonLibs(tracePython) 1790 self.mainThread.tracePythonLibs(tracePython)
1728 1791
1729 # set the system exception handling function to ensure, that 1792 # set the system exception handling function to ensure, that
1730 # we report on all unhandled exceptions 1793 # we report on all unhandled exceptions
1731 sys.excepthook = self.__unhandled_exception 1794 sys.excepthook = self.__unhandled_exception
1732 self.__interceptSignals() 1795 self.__interceptSignals()
1733 1796
1734 # now start debugging 1797 # now start debugging
1735 if enableTrace: 1798 if enableTrace:
1736 self.mainThread.set_trace() 1799 self.mainThread.set_trace()
1737 1800
1738 def startProgInDebugger(self, progargs, wd='', host=None, 1801 def startProgInDebugger(
1739 port=None, exceptions=True, tracePython=False, 1802 self,
1740 redirect=True, passive=True, 1803 progargs,
1741 multiprocessSupport=False, codeStr="", 1804 wd="",
1742 scriptModule=""): 1805 host=None,
1806 port=None,
1807 exceptions=True,
1808 tracePython=False,
1809 redirect=True,
1810 passive=True,
1811 multiprocessSupport=False,
1812 codeStr="",
1813 scriptModule="",
1814 ):
1743 """ 1815 """
1744 Public method used to start the remote debugger. 1816 Public method used to start the remote debugger.
1745 1817
1746 @param progargs commandline for the program to be debugged 1818 @param progargs commandline for the program to be debugged
1747 (list of strings) 1819 (list of strings)
1748 @param wd working directory for the program execution (string) 1820 @param wd working directory for the program execution (string)
1749 @param host hostname of the debug server (string) 1821 @param host hostname of the debug server (string)
1750 @param port portnumber of the debug server (int) 1822 @param port portnumber of the debug server (int)
1765 @type str 1837 @type str
1766 @return exit code of the debugged program 1838 @return exit code of the debugged program
1767 @rtype int 1839 @rtype int
1768 """ 1840 """
1769 if host is None: 1841 if host is None:
1770 host = os.getenv('ERICHOST', 'localhost') 1842 host = os.getenv("ERICHOST", "localhost")
1771 if port is None: 1843 if port is None:
1772 port = os.getenv('ERICPORT', 42424) 1844 port = os.getenv("ERICPORT", 42424)
1773 1845
1774 remoteAddress = self.__resolveHost(host) 1846 remoteAddress = self.__resolveHost(host)
1775 if progargs: 1847 if progargs:
1776 if not progargs[0].startswith("-"): 1848 if not progargs[0].startswith("-"):
1777 name = os.path.basename(progargs[0]) 1849 name = os.path.basename(progargs[0])
1778 elif progargs[0] == "--multiprocessing-fork": 1850 elif progargs[0] == "--multiprocessing-fork":
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)

eric ide

mercurial