DebugClients/Python3/DebugClientBase.py

branch
debugger speed
changeset 5174
8c48f5e0cd92
parent 5170
fb9168c2e069
parent 5171
f1e9eebd5469
equal deleted inserted replaced
5170:fb9168c2e069 5174:8c48f5e0cd92
11 import socket 11 import socket
12 import select 12 import select
13 import codeop 13 import codeop
14 import traceback 14 import traceback
15 import os 15 import os
16 import json
16 import imp 17 import imp
17 import re 18 import re
18 import atexit 19 import atexit
19 import signal 20 import signal
20 21
21 22
22 import DebugProtocol
23 import DebugClientCapabilities 23 import DebugClientCapabilities
24 import DebugVariables
24 from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__ 25 from DebugBase import setRecursionLimit, printerr # __IGNORE_WARNING__
25 from AsyncFile import AsyncFile, AsyncPendingWrite 26 from AsyncFile import AsyncFile, AsyncPendingWrite
26 from DebugConfig import ConfigVarTypeStrings 27 from DebugConfig import ConfigVarTypeStrings
27 from FlexCompleter import Completer 28 from FlexCompleter import Completer
28 from DebugUtilities import getargvalues, formatargvalues 29 from DebugUtilities import getargvalues, formatargvalues, prepareJsonCommand
29 from BreakpointWatch import Breakpoint, Watch 30 from BreakpointWatch import Breakpoint, Watch
30 31
31 32
32 DebugClientInstance = None 33 DebugClientInstance = None
33 34
121 class DebugClientBase(object): 122 class DebugClientBase(object):
122 """ 123 """
123 Class implementing the client side of the debugger. 124 Class implementing the client side of the debugger.
124 125
125 It provides access to the Python interpeter from a debugger running in 126 It provides access to the Python interpeter from a debugger running in
126 another process whether or not the Qt event loop is running. 127 another process.
127 128
128 The protocol between the debugger and the client assumes that there will be 129 The protocol between the debugger and the client is based on JSONRPC 2.0
129 a single source of debugger commands and a single source of Python 130 PDUs. Each one is sent on a single line, i.e. commands or responses are
130 statements. Commands and statement are always exactly one line and may be 131 separated by a linefeed character.
131 interspersed.
132
133 The protocol is as follows. First the client opens a connection to the
134 debugger and then sends a series of one line commands. A command is either
135 >Load<, >Step<, >StepInto<, ... or a Python statement.
136 See DebugProtocol.py for a listing of valid protocol tokens.
137
138 A Python statement consists of the statement to execute, followed (in a
139 separate line) by >OK?<. If the statement was incomplete then the
140 response is >Continue<. If there was an exception then the response
141 is >Exception<. Otherwise the response is >OK<. The reason
142 for the >OK?< part is to provide a sentinal (ie. the responding
143 >OK<) after any possible output as a result of executing the command.
144
145 The client may send any other lines at any other time which should be
146 interpreted as program output.
147 132
148 If the debugger closes the session there is no response from the client. 133 If the debugger closes the session there is no response from the client.
149 The client may close the session at any time as a result of the script 134 The client may close the session at any time as a result of the script
150 being debugged closing or crashing. 135 being debugged closing or crashing.
151 136
152 <b>Note</b>: This class is meant to be subclassed by individual 137 <b>Note</b>: This class is meant to be subclassed by individual
153 DebugClient classes. Do not instantiate it directly. 138 DebugClient classes. Do not instantiate it directly.
154 """ 139 """
155 clientCapabilities = DebugClientCapabilities.HasAll 140 clientCapabilities = DebugClientCapabilities.HasAll
156 141
142 # keep these in sync with VariablesViewer.VariableItem.Indicators
143 Indicators = ("()", "[]", "{:}", "{}") # __IGNORE_WARNING__
144
157 def __init__(self): 145 def __init__(self):
158 """ 146 """
159 Constructor 147 Constructor
160 """ 148 """
161 self.breakpoints = {} 149 self.breakpoints = {}
162 self.redirect = True 150 self.redirect = True
151 self.__receiveBuffer = ""
163 152
164 # The next couple of members are needed for the threaded version. 153 # The next couple of members are needed for the threaded version.
165 # For this base class they contain static values for the non threaded 154 # For this base class they contain static values for the non threaded
166 # debugger 155 # debugger
167 156
186 175
187 # The list of regexp objects to filter variables against 176 # The list of regexp objects to filter variables against
188 self.globalsFilterObjects = [] 177 self.globalsFilterObjects = []
189 self.localsFilterObjects = [] 178 self.localsFilterObjects = []
190 179
191 self.pendingResponse = DebugProtocol.ResponseOK
192 self._fncache = {} 180 self._fncache = {}
193 self.dircache = [] 181 self.dircache = []
194 self.inRawMode = False
195 self.mainProcStr = None # used for the passive mode
196 self.passive = False # used to indicate the passive mode 182 self.passive = False # used to indicate the passive mode
197 self.running = None 183 self.running = None
198 self.test = None 184 self.test = None
199 self.debugging = False 185 self.debugging = False
200 186
260 def attachThread(self, target=None, args=None, kwargs=None, 246 def attachThread(self, target=None, args=None, kwargs=None,
261 mainThread=False): 247 mainThread=False):
262 """ 248 """
263 Public method to setup a thread for DebugClient to debug. 249 Public method to setup a thread for DebugClient to debug.
264 250
265 If mainThread is non-zero, then we are attaching to the already 251 If mainThread is True, then we are attaching to the already
266 started mainthread of the app and the rest of the args are ignored. 252 started mainthread of the app and the rest of the args are ignored.
267 253
268 @param target the start function of the target thread (i.e. the user 254 @param target the start function of the target thread (i.e. the user
269 code) 255 code)
270 @param args arguments to pass to target 256 @param args arguments to pass to target
297 d["broken"] = self.isBroken() 283 d["broken"] = self.isBroken()
298 else: 284 else:
299 d["broken"] = False 285 d["broken"] = False
300 threadList.append(d) 286 threadList.append(d)
301 287
302 self.write("{0}{1!r}\n".format(DebugProtocol.ResponseThreadList, 288 self.sendJsonCommand("ResponseThreadList", {
303 (currentId, threadList))) 289 "currentID": currentId,
290 "threadList": threadList,
291 })
304 292
305 def input(self, prompt, echo=True): 293 def input(self, prompt, echo=True):
306 """ 294 """
307 Public method to implement input() using the event loop. 295 Public method to implement input() using the event loop.
308 296
309 @param prompt the prompt to be shown (string) 297 @param prompt the prompt to be shown (string)
310 @param echo Flag indicating echoing of the input (boolean) 298 @param echo Flag indicating echoing of the input (boolean)
311 @return the entered string 299 @return the entered string
312 """ 300 """
313 self.write("{0}{1!r}\n".format( 301 self.sendJsonCommand("RequestRaw", {
314 DebugProtocol.ResponseRaw, (prompt, echo))) 302 "prompt": prompt,
315 self.inRawMode = True 303 "echo": echo,
304 })
316 self.eventLoop(True) 305 self.eventLoop(True)
317 return self.rawLine 306 return self.rawLine
318 307
319 def __exceptionRaised(self):
320 """
321 Private method called in the case of an exception.
322
323 It ensures that the debug server is informed of the raised exception.
324 """
325 self.pendingResponse = DebugProtocol.ResponseException
326
327 def sessionClose(self, exit=True): 308 def sessionClose(self, exit=True):
328 """ 309 """
329 Public method to close the session with the debugger and optionally 310 Public method to close the session with the debugger and optionally
330 terminate. 311 terminate.
331 312
334 try: 315 try:
335 self.set_quit() 316 self.set_quit()
336 except Exception: 317 except Exception:
337 pass 318 pass
338 319
339 # clean up asyncio.
340 self.disconnect()
341 self.debugging = False 320 self.debugging = False
342 321
343 # make sure we close down our end of the socket 322 # make sure we close down our end of the socket
344 # might be overkill as normally stdin, stdout and stderr 323 # might be overkill as normally stdin, stdout and stderr
345 # SHOULD be closed on exit, but it does not hurt to do it here 324 # SHOULD be closed on exit, but it does not hurt to do it here
367 except SyntaxError: 346 except SyntaxError:
368 exctype, excval, exctb = sys.exc_info() 347 exctype, excval, exctb = sys.exc_info()
369 try: 348 try:
370 message = str(excval) 349 message = str(excval)
371 filename = excval.filename 350 filename = excval.filename
372 linenr = excval.lineno 351 lineno = excval.lineno
373 charnr = excval.offset 352 charno = excval.offset
374 except (AttributeError, ValueError): 353 except (AttributeError, ValueError):
375 exclist = [] 354 message = ""
376 else: 355 filename = ""
377 exclist = [message, [filename, linenr, charnr]] 356 lineno = 0
378 357 charno = 0
379 self.write("{0}{1}\n".format( 358 self.sendSyntaxError(message, filename, lineno, charno)
380 DebugProtocol.ResponseSyntax, str(exclist)))
381 return None 359 return None
382 360
383 return code 361 return code
384 362
385 def handleLine(self, line): 363 def handleLine(self, line):
394 # Remove any newline. 372 # Remove any newline.
395 if line[-1] == '\n': 373 if line[-1] == '\n':
396 line = line[:-1] 374 line = line[:-1]
397 375
398 ## printerr(line) ##debug 376 ## printerr(line) ##debug
399 377
400 eoc = line.find('<') 378 self.handleJsonCommand(line)
401 379
402 if eoc >= 0 and line[0] == '>': 380 def handleJsonCommand(self, jsonStr):
403 # Get the command part and any argument. 381 """
404 cmd = line[:eoc + 1] 382 Public method to handle a command serialized as a JSON string.
405 arg = line[eoc + 1:] 383
406 384 @param jsonStr string containing the command received from the IDE
407 if cmd == DebugProtocol.RequestVariables: 385 @type str
408 frmnr, scope, filter = eval(arg.replace("u'", "'")) 386 """
409 self.__dumpVariables(int(frmnr), int(scope), filter) 387 try:
410 return 388 commandDict = json.loads(jsonStr.strip())
411 389 except (TypeError, ValueError) as err:
412 if cmd == DebugProtocol.RequestVariable: 390 printerr(str(err))
413 var, frmnr, scope, filter = eval(arg.replace("u'", "'")) 391 return
414 self.__dumpVariable(var, int(frmnr), int(scope), filter) 392
415 return 393 method = commandDict["method"]
416 394 params = commandDict["params"]
417 if cmd == DebugProtocol.RequestThreadList: 395
418 self.__dumpThreadList() 396 if method == "RequestVariables":
419 return 397 self.__dumpVariables(
420 398 params["frameNumber"], params["scope"], params["filters"])
421 if cmd == DebugProtocol.RequestThreadSet: 399
422 tid = eval(arg) 400 elif method == "RequestVariable":
423 if tid in self.threads: 401 self.__dumpVariable(
424 self.setCurrentThread(tid) 402 params["variable"], params["frameNumber"],
425 self.write(DebugProtocol.ResponseThreadSet + '\n') 403 params["scope"], params["filters"])
426 stack = self.currentThread.getStack() 404
427 self.write('{0}{1!r}\n'.format( 405 elif method == "RequestThreadList":
428 DebugProtocol.ResponseStack, stack)) 406 self.__dumpThreadList()
429 return 407
430 408 elif method == "RequestThreadSet":
431 if cmd == DebugProtocol.RequestStep: 409 if params["threadID"] in self.threads:
432 self.currentThread.step(True) 410 self.setCurrentThread(params["threadID"])
433 self.eventExit = True 411 self.sendJsonCommand("ResponseThreadSet", {})
434 return 412 stack = self.currentThread.getStack()
435 413 self.sendJsonCommand("ResponseStack", {
436 if cmd == DebugProtocol.RequestStepOver: 414 "stack": stack,
437 self.currentThread.step(False) 415 })
438 self.eventExit = True 416
439 return 417 elif method == "RequestCapabilities":
440 418 self.sendJsonCommand("ResponseCapabilities", {
441 if cmd == DebugProtocol.RequestStepOut: 419 "capabilities": self.__clientCapabilities(),
442 self.currentThread.stepOut() 420 "clientType": "Python3"
443 self.eventExit = True 421 })
444 return 422
445 423 elif method == "RequestBanner":
446 if cmd == DebugProtocol.RequestStepQuit: 424 self.sendJsonCommand("ResponseBanner", {
447 if self.passive: 425 "version": "Python {0}".format(sys.version),
448 self.progTerminated(42) 426 "platform": socket.gethostname(),
427 "dbgclient": self.variant,
428 })
429
430 elif method == "RequestSetFilter":
431 self.__generateFilterObjects(params["scope"], params["filter"])
432
433 elif method == "RequestCallTrace":
434 if params["enable"]:
435 callTraceEnabled = self.profile
436 else:
437 callTraceEnabled = None
438
439 if self.debugging:
440 sys.setprofile(callTraceEnabled)
441 else:
442 # remember for later
443 self.callTraceEnabled = callTraceEnabled
444
445 elif method == "RequestEnvironment":
446 for key, value in params["environment"].items():
447 if key.endswith("+"):
448 if key[:-1] in os.environ:
449 os.environ[key[:-1]] += value
450 else:
451 os.environ[key[:-1]] = value
449 else: 452 else:
450 self.set_quit() 453 os.environ[key] = value
451 self.eventExit = True 454
452 return 455 elif method == "RequestLoad":
453 456 self._fncache = {}
454 if cmd == DebugProtocol.RequestContinue: 457 self.dircache = []
455 special = int(arg) 458 sys.argv = []
456 self.currentThread.go(special) 459 self.__setCoding(params["filename"])
457 self.eventExit = True 460 sys.argv.append(params["filename"])
458 return 461 sys.argv.extend(params["argv"])
459 462 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
460 if cmd == DebugProtocol.RequestOK: 463 if params["workdir"] == '':
461 self.write(self.pendingResponse + '\n') 464 os.chdir(sys.path[1])
462 self.pendingResponse = DebugProtocol.ResponseOK 465 else:
463 return 466 os.chdir(params["workdir"])
464 467
465 if cmd == DebugProtocol.RequestCallTrace: 468 self.running = sys.argv[0]
466 if arg.strip().lower() == "on": 469 self.mainFrame = None
467 callTraceEnabled = self.profile 470 self.debugging = True
471
472 self.fork_auto = params["autofork"]
473 self.fork_child = params["forkChild"]
474
475 self.threads.clear()
476 self.attachThread(mainThread=True)
477
478 # set the system exception handling function to ensure, that
479 # we report on all unhandled exceptions
480 sys.excepthook = self.__unhandled_exception
481 self.__interceptSignals()
482
483 # clear all old breakpoints, they'll get set after we have
484 # started
485 Breakpoint.clear_all_breaks()
486 Watch.clear_all_watches()
487
488 self.mainThread.tracePythonLibs(params["traceInterpreter"])
489
490 # This will eventually enter a local event loop.
491 self.debugMod.__dict__['__file__'] = self.running
492 sys.modules['__main__'] = self.debugMod
493 code = self.__compileFileSource(self.running)
494 if code:
495 sys.setprofile(self.callTraceEnabled)
496 res = self.mainThread.run(code, self.debugMod.__dict__)
497 self.progTerminated(res)
498
499 elif method == "RequestRun":
500 sys.argv = []
501 self.__setCoding(params["filename"])
502 sys.argv.append(params["filename"])
503 sys.argv.extend(params["argv"])
504 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
505 if params["workdir"] == '':
506 os.chdir(sys.path[1])
507 else:
508 os.chdir(params["workdir"])
509
510 self.running = sys.argv[0]
511 self.mainFrame = None
512 self.botframe = None
513
514 self.fork_auto = params["autofork"]
515 self.fork_child = params["forkChild"]
516
517 self.threads.clear()
518 self.attachThread(mainThread=True)
519
520 # set the system exception handling function to ensure, that
521 # we report on all unhandled exceptions
522 sys.excepthook = self.__unhandled_exception
523 self.__interceptSignals()
524
525 self.mainThread.tracePythonLibs(False)
526
527 self.debugMod.__dict__['__file__'] = sys.argv[0]
528 sys.modules['__main__'] = self.debugMod
529 res = 0
530 code = self.__compileFileSource(self.running)
531 if code:
532 try:
533 exec(code, self.debugMod.__dict__)
534 except SystemExit as exc:
535 res = exc.code
536 atexit._run_exitfuncs()
537 self.writestream.flush()
538 self.progTerminated(res)
539
540 elif method == "RequestCoverage":
541 from coverage import coverage
542 sys.argv = []
543 self.__setCoding(params["filename"])
544 sys.argv.append(params["filename"])
545 sys.argv.extend(params["argv"])
546 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
547 if params["workdir"] == '':
548 os.chdir(sys.path[1])
549 else:
550 os.chdir(params["workdir"])
551
552 # set the system exception handling function to ensure, that
553 # we report on all unhandled exceptions
554 sys.excepthook = self.__unhandled_exception
555 self.__interceptSignals()
556
557 # generate a coverage object
558 self.cover = coverage(
559 auto_data=True,
560 data_file="{0}.coverage".format(
561 os.path.splitext(sys.argv[0])[0]))
562
563 if params["erase"]:
564 self.cover.erase()
565 sys.modules['__main__'] = self.debugMod
566 self.debugMod.__dict__['__file__'] = sys.argv[0]
567 fp = open(sys.argv[0], encoding=self.__coding)
568 try:
569 script = fp.read()
570 finally:
571 fp.close()
572 if script:
573 if not script.endswith('\n'):
574 script += '\n'
575 code = compile(script, sys.argv[0], 'exec')
576 self.running = sys.argv[0]
577 res = 0
578 self.cover.start()
579 try:
580 exec(code, self.debugMod.__dict__)
581 except SystemExit as exc:
582 res = exc.code
583 atexit._run_exitfuncs()
584 self.cover.stop()
585 self.cover.save()
586 self.writestream.flush()
587 self.progTerminated(res)
588
589 elif method == "RequestProfile":
590 sys.setprofile(None)
591 import PyProfile
592 sys.argv = []
593 self.__setCoding(params["filename"])
594 sys.argv.append(params["filename"])
595 sys.argv.extend(params["argv"])
596 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
597 if params["workdir"] == '':
598 os.chdir(sys.path[1])
599 else:
600 os.chdir(params["workdir"])
601
602 # set the system exception handling function to ensure, that
603 # we report on all unhandled exceptions
604 sys.excepthook = self.__unhandled_exception
605 self.__interceptSignals()
606
607 # generate a profile object
608 self.prof = PyProfile.PyProfile(sys.argv[0])
609
610 if params["erase"]:
611 self.prof.erase()
612 self.debugMod.__dict__['__file__'] = sys.argv[0]
613 sys.modules['__main__'] = self.debugMod
614 fp = open(sys.argv[0], encoding=self.__coding)
615 try:
616 script = fp.read()
617 finally:
618 fp.close()
619 if script:
620 if not script.endswith('\n'):
621 script += '\n'
622 self.running = sys.argv[0]
623 res = 0
624 try:
625 self.prof.run(script)
626 except SystemExit as exc:
627 res = exc.code
628 atexit._run_exitfuncs()
629 self.prof.save()
630 self.writestream.flush()
631 self.progTerminated(res)
632
633 elif method == "ExecuteStatement":
634 if self.buffer:
635 self.buffer = self.buffer + '\n' + params["statement"]
636 else:
637 self.buffer = params["statement"]
638
639 try:
640 code = self.compile_command(self.buffer, self.readstream.name)
641 except (OverflowError, SyntaxError, ValueError):
642 # Report the exception
643 sys.last_type, sys.last_value, sys.last_traceback = \
644 sys.exc_info()
645 self.sendJsonCommand("ClientOutput", {
646 "text": "".join(traceback.format_exception_only(
647 sys.last_type, sys.last_value))
648 })
649 self.buffer = ''
650 else:
651 if code is None:
652 self.sendJsonCommand("ResponseContinue", {})
653 return
468 else: 654 else:
469 callTraceEnabled = None 655 self.buffer = ''
470 if self.debugging: 656
471 sys.setprofile(callTraceEnabled) 657 try:
472 else: 658 if self.running is None:
473 # remember for later 659 exec(code, self.debugMod.__dict__)
474 self.callTraceEnabled = callTraceEnabled
475
476 return
477
478 if cmd == DebugProtocol.RequestEnv:
479 env = eval(arg.replace("u'", "'"))
480 for key, value in env.items():
481 if key.endswith("+"):
482 if key[:-1] in os.environ:
483 os.environ[key[:-1]] += value
484 else: 660 else:
485 os.environ[key[:-1]] = value 661 if self.currentThread is None:
486 else: 662 # program has terminated
487 os.environ[key] = value
488 return
489
490 if cmd == DebugProtocol.RequestLoad:
491 self._fncache = {}
492 self.dircache = []
493 sys.argv = []
494 wd, fn, args, tracePython = arg.split('|')
495 self.__setCoding(fn)
496 sys.argv.append(fn)
497 sys.argv.extend(eval(args.replace("u'", "'")))
498 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
499 if wd == '':
500 os.chdir(sys.path[1])
501 else:
502 os.chdir(wd)
503 self.running = sys.argv[0]
504 self.mainFrame = None
505 self.inRawMode = False
506 self.debugging = True
507
508 self.threads.clear()
509 self.attachThread(mainThread=True)
510
511 # set the system exception handling function to ensure, that
512 # we report on all unhandled exceptions
513 sys.excepthook = self.__unhandled_exception
514 self.__interceptSignals()
515
516 # clear all old breakpoints, they'll get set after we have
517 # started
518 Breakpoint.clear_all_breaks()
519 Watch.clear_all_watches()
520
521 self.mainThread.tracePythonLibs(int(tracePython))
522
523 # This will eventually enter a local event loop.
524 # Note the use of backquotes to cause a repr of self.running.
525 # The need for this is on Windows os where backslash is the
526 # path separator. They will get inadvertantly stripped away
527 # during the eval causing IOErrors, if self.running is passed
528 # as a normal str.
529 self.debugMod.__dict__['__file__'] = self.running
530 sys.modules['__main__'] = self.debugMod
531 code = self.__compileFileSource(self.running)
532 if code:
533 sys.setprofile(self.callTraceEnabled)
534 res = self.mainThread.run(code, self.debugMod.__dict__)
535 self.progTerminated(res)
536 return
537
538 if cmd == DebugProtocol.RequestRun:
539 sys.argv = []
540 wd, fn, args = arg.split('|')
541 self.__setCoding(fn)
542 sys.argv.append(fn)
543 sys.argv.extend(eval(args.replace("u'", "'")))
544 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
545 if wd == '':
546 os.chdir(sys.path[1])
547 else:
548 os.chdir(wd)
549
550 self.running = sys.argv[0]
551 self.mainFrame = None
552 self.botframe = None
553 self.inRawMode = False
554
555 self.threads.clear()
556 self.attachThread(mainThread=True)
557
558 # set the system exception handling function to ensure, that
559 # we report on all unhandled exceptions
560 sys.excepthook = self.__unhandled_exception
561 self.__interceptSignals()
562
563 self.mainThread.tracePythonLibs(0)
564
565 self.debugMod.__dict__['__file__'] = sys.argv[0]
566 sys.modules['__main__'] = self.debugMod
567 res = 0
568 code = self.__compileFileSource(self.running)
569 if code:
570 try:
571 exec(code, self.debugMod.__dict__)
572 except SystemExit as exc:
573 res = exc.code
574 atexit._run_exitfuncs()
575 self.writestream.flush()
576 self.progTerminated(res)
577 return
578
579 if cmd == DebugProtocol.RequestProfile:
580 sys.setprofile(None)
581 import PyProfile
582 sys.argv = []
583 wd, fn, args, erase = arg.split('|')
584 self.__setCoding(fn)
585 sys.argv.append(fn)
586 sys.argv.extend(eval(args.replace("u'", "'")))
587 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
588 if wd == '':
589 os.chdir(sys.path[1])
590 else:
591 os.chdir(wd)
592
593 # set the system exception handling function to ensure, that
594 # we report on all unhandled exceptions
595 sys.excepthook = self.__unhandled_exception
596 self.__interceptSignals()
597
598 # generate a profile object
599 self.prof = PyProfile.PyProfile(sys.argv[0])
600
601 if int(erase):
602 self.prof.erase()
603 self.debugMod.__dict__['__file__'] = sys.argv[0]
604 sys.modules['__main__'] = self.debugMod
605 fp = open(sys.argv[0], encoding=self.__coding)
606 try:
607 script = fp.read()
608 finally:
609 fp.close()
610 if script:
611 if not script.endswith('\n'):
612 script += '\n'
613 self.running = sys.argv[0]
614 res = 0
615 try:
616 self.prof.run(script)
617 except SystemExit as exc:
618 res = exc.code
619 atexit._run_exitfuncs()
620 self.prof.save()
621 self.writestream.flush()
622 self.progTerminated(res)
623 return
624
625 if cmd == DebugProtocol.RequestCoverage:
626 from coverage import coverage
627 sys.argv = []
628 wd, fn, args, erase = arg.split('@@')
629 self.__setCoding(fn)
630 sys.argv.append(fn)
631 sys.argv.extend(eval(args.replace("u'", "'")))
632 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
633 if wd == '':
634 os.chdir(sys.path[1])
635 else:
636 os.chdir(wd)
637
638 # set the system exception handling function to ensure, that
639 # we report on all unhandled exceptions
640 sys.excepthook = self.__unhandled_exception
641 self.__interceptSignals()
642
643 # generate a coverage object
644 self.cover = coverage(
645 auto_data=True,
646 data_file="{0}.coverage".format(
647 os.path.splitext(sys.argv[0])[0]))
648
649 if int(erase):
650 self.cover.erase()
651 sys.modules['__main__'] = self.debugMod
652 self.debugMod.__dict__['__file__'] = sys.argv[0]
653 fp = open(sys.argv[0], encoding=self.__coding)
654 try:
655 script = fp.read()
656 finally:
657 fp.close()
658 if script:
659 if not script.endswith('\n'):
660 script += '\n'
661 code = compile(script, sys.argv[0], 'exec')
662 self.running = sys.argv[0]
663 res = 0
664 self.cover.start()
665 try:
666 exec(code, self.debugMod.__dict__)
667 except SystemExit as exc:
668 res = exc.code
669 atexit._run_exitfuncs()
670 self.cover.stop()
671 self.cover.save()
672 self.writestream.flush()
673 self.progTerminated(res)
674 return
675
676 if cmd == DebugProtocol.RequestShutdown:
677 self.sessionClose()
678 return
679
680 if cmd == DebugProtocol.RequestBreak:
681 fn, line, temporary, set, cond = arg.split('@@')
682 line = int(line)
683 set = int(set)
684 temporary = int(temporary)
685
686 if set:
687 if cond == 'None' or cond == '':
688 cond = None
689 else:
690 try:
691 cond = compile(cond, '<string>', 'eval')
692 except SyntaxError:
693 self.write('{0}{1},{2:d}\n'.format(
694 DebugProtocol.ResponseBPConditionError,
695 fn, line))
696 return
697 Breakpoint(fn, line, temporary, cond)
698 else:
699 Breakpoint.clear_break(fn, line)
700
701 return
702
703 if cmd == DebugProtocol.RequestBreakEnable:
704 fn, line, enable = arg.split(',')
705 line = int(line)
706 enable = int(enable)
707
708 bp = Breakpoint.get_break(fn, line)
709 if bp is not None:
710 if enable:
711 bp.enable()
712 else:
713 bp.disable()
714
715 return
716
717 if cmd == DebugProtocol.RequestBreakIgnore:
718 fn, line, count = arg.split(',')
719 line = int(line)
720 count = int(count)
721
722 bp = Breakpoint.get_break(fn, line)
723 if bp is not None:
724 bp.ignore = count
725
726 return
727
728 if cmd == DebugProtocol.RequestWatch:
729 cond, temporary, set = arg.split('@@')
730 set = int(set)
731 temporary = int(temporary)
732
733 if cond.endswith(('??created??', '??changed??')):
734 compiledCond, flag = cond.split()
735 else:
736 compiledCond = cond
737 flag = ''
738
739 try:
740 compiledCond = compile(compiledCond, '<string>', 'eval')
741 except SyntaxError:
742 self.write('{0}{1}\n'.format(
743 DebugProtocol.ResponseWPConditionError, cond))
744 return
745
746 if set:
747 Watch(cond, compiledCond, flag, temporary)
748 else:
749 Watch.clear_watch(cond)
750
751 return
752
753 if cmd == DebugProtocol.RequestWatchEnable:
754 cond, enable = arg.split(',')
755 enable = int(enable)
756
757 bp = Watch.get_watch(cond)
758 if bp is not None:
759 if enable:
760 bp.enable()
761 else:
762 bp.disable()
763
764 return
765
766 if cmd == DebugProtocol.RequestWatchIgnore:
767 cond, count = arg.split(',')
768 count = int(count)
769
770 bp = Watch.get_watch(cond)
771 if bp is not None:
772 bp.ignore = count
773
774 return
775
776 if cmd == DebugProtocol.RequestEval:
777 try:
778 value = eval(
779 arg,
780 self.currentThread.getCurrentFrame().f_globals,
781 self.currentThread.getFrameLocals(self.framenr))
782 self.currentThread.storeFrameLocals(self.framenr)
783 except Exception:
784 # Report the exception and the traceback
785 try:
786 type, value, tb = sys.exc_info()
787 sys.last_type = type
788 sys.last_value = value
789 sys.last_traceback = tb
790 tblist = traceback.extract_tb(tb)
791 del tblist[:1]
792 list = traceback.format_list(tblist)
793 if list:
794 list.insert(0, "Traceback (innermost last):\n")
795 list[len(list):] = \
796 traceback.format_exception_only(type, value)
797 finally:
798 tblist = tb = None
799
800 for l in list:
801 self.write(l)
802
803 self.write(DebugProtocol.ResponseException + '\n')
804
805 else:
806 self.write(str(value) + '\n')
807 self.write(DebugProtocol.ResponseOK + '\n')
808
809 return
810
811 if cmd == DebugProtocol.RequestExec:
812 _globals = self.currentThread.getCurrentFrame().f_globals
813 _locals = self.currentThread.getFrameLocals(self.framenr)
814 try:
815 code = compile(arg + '\n', '<stdin>', 'single')
816 exec(code, _globals, _locals)
817 self.currentThread.storeFrameLocals(self.framenr)
818 except Exception:
819 # Report the exception and the traceback
820 try:
821 type, value, tb = sys.exc_info()
822 sys.last_type = type
823 sys.last_value = value
824 sys.last_traceback = tb
825 tblist = traceback.extract_tb(tb)
826 del tblist[:1]
827 list = traceback.format_list(tblist)
828 if list:
829 list.insert(0, "Traceback (innermost last):\n")
830 list[len(list):] = \
831 traceback.format_exception_only(type, value)
832 finally:
833 tblist = tb = None
834
835 for l in list:
836 self.write(l)
837
838 self.write(DebugProtocol.ResponseException + '\n')
839
840 return
841
842 if cmd == DebugProtocol.RequestBanner:
843 self.write('{0}{1}\n'.format(DebugProtocol.ResponseBanner,
844 str(("Python {0}".format(sys.version),
845 socket.gethostname(), self.variant))))
846 return
847
848 if cmd == DebugProtocol.RequestCapabilities:
849 self.write('{0}{1:d}, "Python3"\n'.format(
850 DebugProtocol.ResponseCapabilities,
851 self.__clientCapabilities()))
852 return
853
854 if cmd == DebugProtocol.RequestCompletion:
855 self.__completionList(arg.replace("u'", "'"))
856 return
857
858 if cmd == DebugProtocol.RequestSetFilter:
859 scope, filterString = eval(arg.replace("u'", "'"))
860 self.__generateFilterObjects(int(scope), filterString)
861 return
862
863 if cmd == DebugProtocol.RequestUTPrepare:
864 fn, tn, tfn, failed, cov, covname, erase = arg.split('|')
865 sys.path.insert(0, os.path.dirname(os.path.abspath(fn)))
866 os.chdir(sys.path[0])
867 failed = eval(failed)
868
869 # set the system exception handling function to ensure, that
870 # we report on all unhandled exceptions
871 sys.excepthook = self.__unhandled_exception
872 self.__interceptSignals()
873
874 try:
875 import unittest
876 utModule = imp.load_source(tn, fn)
877 try:
878 if failed:
879 self.test = unittest.defaultTestLoader\
880 .loadTestsFromNames(failed, utModule)
881 else:
882 self.test = unittest.defaultTestLoader\
883 .loadTestsFromName(tfn, utModule)
884 except AttributeError:
885 self.test = unittest.defaultTestLoader\
886 .loadTestsFromModule(utModule)
887 except Exception:
888 exc_type, exc_value, exc_tb = sys.exc_info()
889 self.write('{0}{1}\n'.format(
890 DebugProtocol.ResponseUTPrepared,
891 str((0, str(exc_type), str(exc_value)))))
892 self.__exceptionRaised()
893 return
894
895 # generate a coverage object
896 if int(cov):
897 from coverage import coverage
898 self.cover = coverage(
899 auto_data=True,
900 data_file="{0}.coverage".format(
901 os.path.splitext(covname)[0]))
902 if int(erase):
903 self.cover.erase()
904 else:
905 self.cover = None
906
907 self.write('{0}{1}\n'.format(
908 DebugProtocol.ResponseUTPrepared,
909 str((self.test.countTestCases(), "", ""))))
910 return
911
912 if cmd == DebugProtocol.RequestUTRun:
913 from DCTestResult import DCTestResult
914 self.testResult = DCTestResult(self)
915 if self.cover:
916 self.cover.start()
917 self.test.run(self.testResult)
918 if self.cover:
919 self.cover.stop()
920 self.cover.save()
921 self.write('{0}\n'.format(DebugProtocol.ResponseUTFinished))
922 return
923
924 if cmd == DebugProtocol.RequestUTStop:
925 self.testResult.stop()
926 return
927
928 if cmd == DebugProtocol.ResponseForkTo:
929 # this results from a separate event loop
930 self.fork_child = (arg == 'child')
931 self.eventExit = True
932 return
933
934 if cmd == DebugProtocol.RequestForkMode:
935 self.fork_auto, self.fork_child = eval(arg)
936 return
937
938 # If we are handling raw mode input then reset the mode and break out
939 # of the current event loop.
940 if self.inRawMode:
941 self.inRawMode = False
942 self.rawLine = line
943 self.eventExit = True
944 return
945
946 if self.buffer:
947 self.buffer = self.buffer + '\n' + line
948 else:
949 self.buffer = line
950
951 try:
952 code = self.compile_command(self.buffer, self.readstream.name)
953 except (OverflowError, SyntaxError, ValueError):
954 # Report the exception
955 sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info()
956 for l in traceback.format_exception_only(
957 sys.last_type, sys.last_value):
958 self.write(l)
959 self.buffer = ''
960 else:
961 if code is None:
962 self.pendingResponse = DebugProtocol.ResponseContinue
963 else:
964 self.buffer = ''
965
966 try:
967 if self.running is None:
968 exec(code, self.debugMod.__dict__)
969 else:
970 if self.currentThread is None:
971 # program has terminated
972 self.running = None
973 _globals = self.debugMod.__dict__
974 _locals = _globals
975 else:
976 cf = self.currentThread.getCurrentFrame()
977 # program has terminated
978 if cf is None:
979 self.running = None 663 self.running = None
980 _globals = self.debugMod.__dict__ 664 _globals = self.debugMod.__dict__
981 _locals = _globals 665 _locals = _globals
982 else: 666 else:
983 frmnr = self.framenr 667 cf = self.currentThread.getCurrentFrame()
984 while cf is not None and frmnr > 0: 668 # program has terminated
985 cf = cf.f_back 669 if cf is None:
986 frmnr -= 1 670 self.running = None
987 _globals = cf.f_globals 671 _globals = self.debugMod.__dict__
988 _locals = \ 672 _locals = _globals
989 self.currentThread.getFrameLocals( 673 else:
990 self.framenr) 674 frmnr = self.framenr
991 # reset sys.stdout to our redirector (unconditionally) 675 while cf is not None and frmnr > 0:
992 if "sys" in _globals: 676 cf = cf.f_back
993 __stdout = _globals["sys"].stdout 677 frmnr -= 1
994 _globals["sys"].stdout = self.writestream 678 _globals = cf.f_globals
995 exec(code, _globals, _locals) 679 _locals = \
996 _globals["sys"].stdout = __stdout 680 self.currentThread.getFrameLocals(
997 elif "sys" in _locals: 681 self.framenr)
998 __stdout = _locals["sys"].stdout 682 # reset sys.stdout to our redirector
999 _locals["sys"].stdout = self.writestream 683 # (unconditionally)
1000 exec(code, _globals, _locals) 684 if "sys" in _globals:
1001 _locals["sys"].stdout = __stdout 685 __stdout = _globals["sys"].stdout
1002 else: 686 _globals["sys"].stdout = self.writestream
1003 exec(code, _globals, _locals) 687 exec(code, _globals, _locals)
1004 688 _globals["sys"].stdout = __stdout
1005 self.currentThread.storeFrameLocals(self.framenr) 689 elif "sys" in _locals:
1006 except SystemExit as exc: 690 __stdout = _locals["sys"].stdout
1007 self.progTerminated(exc.code) 691 _locals["sys"].stdout = self.writestream
1008 except Exception: 692 exec(code, _globals, _locals)
1009 # Report the exception and the traceback 693 _locals["sys"].stdout = __stdout
694 else:
695 exec(code, _globals, _locals)
696
697 self.currentThread.storeFrameLocals(self.framenr)
698 except SystemExit as exc:
699 self.progTerminated(exc.code)
700 except Exception:
701 # Report the exception and the traceback
702 tlist = []
703 try:
704 exc_type, exc_value, exc_tb = sys.exc_info()
705 sys.last_type = exc_type
706 sys.last_value = exc_value
707 sys.last_traceback = exc_tb
708 tblist = traceback.extract_tb(exc_tb)
709 del tblist[:1]
710 tlist = traceback.format_list(tblist)
711 if tlist:
712 tlist.insert(
713 0, "Traceback (innermost last):\n")
714 tlist.extend(traceback.format_exception_only(
715 exc_type, exc_value))
716 finally:
717 tblist = exc_tb = None
718
719 self.sendJsonCommand("ClientOutput", {
720 "text": "".join(tlist)
721 })
722
723 self.sendJsonCommand("ResponseOK", {})
724
725 elif method == "RequestStep":
726 self.currentThread.step(True)
727 self.eventExit = True
728
729 elif method == "RequestStepOver":
730 self.currentThread.step(False)
731 self.eventExit = True
732
733 elif method == "RequestStepOut":
734 self.currentThread.stepOut()
735 self.eventExit = True
736
737 elif method == "RequestStepQuit":
738 if self.passive:
739 self.progTerminated(42)
740 else:
741 self.set_quit()
742 self.eventExit = True
743
744 elif method == "RequestContinue":
745 self.currentThread.go(params["special"])
746 self.eventExit = True
747
748 elif method == "RawInput":
749 # If we are handling raw mode input then break out of the current
750 # event loop.
751 self.rawLine = params["input"]
752 self.eventExit = True
753
754 elif method == "RequestBreakpoint":
755 if params["setBreakpoint"]:
756 if params["condition"] in ['None', '']:
757 cond = None
758 elif params["condition"] is not None:
1010 try: 759 try:
1011 exc_type, exc_value, exc_tb = sys.exc_info() 760 cond = compile(params["condition"], '<string>', 'eval')
1012 sys.last_type = exc_type 761 except SyntaxError:
1013 sys.last_value = exc_value 762 self.sendJsonCommand("ResponseBPConditionError", {
1014 sys.last_traceback = exc_tb 763 "filename": params["filename"],
1015 tblist = traceback.extract_tb(exc_tb) 764 "line": params["line"],
1016 del tblist[:1] 765 })
1017 list = traceback.format_list(tblist) 766 return
1018 if list: 767 else:
1019 list.insert(0, "Traceback (innermost last):\n") 768 cond = None
1020 list[len(list):] = traceback.format_exception_only( 769
1021 exc_type, exc_value) 770 Breakpoint(
1022 finally: 771 params["filename"], params["line"], params["temporary"],
1023 tblist = exc_tb = None 772 cond)
1024 773 else:
1025 for l in list: 774 Breakpoint.clear_break(params["filename"], params["line"])
1026 self.write(l) 775
1027 776 elif method == "RequestBreakpointEnable":
777 bp = Breakpoint.get_break(params["filename"], params["line"])
778 if bp is not None:
779 if params["enable"]:
780 bp.enable()
781 else:
782 bp.disable()
783
784 elif method == "RequestBreakpointIgnore":
785 bp = Breakpoint.get_break(params["filename"], params["line"])
786 if bp is not None:
787 bp.ignore = params["count"]
788
789 elif method == "RequestWatch":
790 if params["setWatch"]:
791 if params["condition"].endswith(
792 ('??created??', '??changed??')):
793 compiledCond, flag = params["condition"].split()
794 else:
795 compiledCond = params["condition"]
796 flag = ''
797
798 try:
799 compiledCond = compile(
800 compiledCond, '<string>', 'eval')
801 except SyntaxError:
802 self.sendJsonCommand("ResponseWatchConditionError", {
803 "condition": params["condition"],
804 })
805 return
806 Watch(
807 params["condition"], compiledCond, flag,
808 params["temporary"])
809 else:
810 Watch.clear_watch(params["condition"])
811
812 elif method == "RequestWatchEnable":
813 wp = Watch.get_watch(params["condition"])
814 if wp is not None:
815 if params["enable"]:
816 wp.enable()
817 else:
818 wp.disable()
819
820 elif method == "RequestWatchIgnore":
821 wp = Watch.get_watch(params["condition"])
822 if wp is not None:
823 wp.ignore = params["count"]
824
825 elif method == "RequestShutdown":
826 self.sessionClose()
827
828 elif method == "RequestCompletion":
829 self.__completionList(params["text"])
830
831 elif method == "RequestUTPrepare":
832 sys.path.insert(
833 0, os.path.dirname(os.path.abspath(params["filename"])))
834 os.chdir(sys.path[0])
835
836 # set the system exception handling function to ensure, that
837 # we report on all unhandled exceptions
838 sys.excepthook = self.__unhandled_exception
839 self.__interceptSignals()
840
841 try:
842 import unittest
843 utModule = imp.load_source(
844 params["testname"], params["filename"])
845 try:
846 if params["failed"]:
847 self.test = unittest.defaultTestLoader\
848 .loadTestsFromNames(params["failed"], utModule)
849 else:
850 self.test = unittest.defaultTestLoader\
851 .loadTestsFromName(params["testfunctionname"],
852 utModule)
853 except AttributeError:
854 self.test = unittest.defaultTestLoader\
855 .loadTestsFromModule(utModule)
856 except Exception:
857 exc_type, exc_value, exc_tb = sys.exc_info()
858 self.sendJsonCommand("ResponseUTPrepared", {
859 "count": 0,
860 "exception": exc_type.__name__,
861 "message": str(exc_value),
862 })
863 return
864
865 # generate a coverage object
866 if params["coverage"]:
867 from coverage import coverage
868 self.cover = coverage(
869 auto_data=True,
870 data_file="{0}.coverage".format(
871 os.path.splitext(params["coveragefile"])[0]))
872 if params["coverageerase"]:
873 self.cover.erase()
874 else:
875 self.cover = None
876
877 self.sendJsonCommand("ResponseUTPrepared", {
878 "count": self.test.countTestCases(),
879 "exception": "",
880 "message": "",
881 })
882
883 elif method == "RequestUTRun":
884 from DCTestResult import DCTestResult
885 self.testResult = DCTestResult(self)
886 if self.cover:
887 self.cover.start()
888 self.test.run(self.testResult)
889 if self.cover:
890 self.cover.stop()
891 self.cover.save()
892 self.sendJsonCommand("ResponseUTFinished", {})
893
894 elif method == "RequestUTStop":
895 self.testResult.stop()
896
897 elif method == "ResponseForkTo":
898 # this results from a separate event loop
899 self.fork_child = (params["target"] == 'child')
900 self.eventExit = True
901
902 def sendJsonCommand(self, method, params):
903 """
904 Public method to send a single command or response to the IDE.
905
906 @param method command or response command name to be sent
907 @type str
908 @param params dictionary of named parameters for the command or
909 response
910 @type dict
911 """
912 cmd = prepareJsonCommand(method, params)
913
914 self.writestream.write_p(cmd)
915 self.writestream.flush()
916
917 def sendClearTemporaryBreakpoint(self, filename, lineno):
918 """
919 Public method to signal the deletion of a temporary breakpoint.
920
921 @param filename name of the file the bp belongs to
922 @type str
923 @param lineno linenumber of the bp
924 @type int
925 """
926 self.sendJsonCommand("ResponseClearBreakpoint", {
927 "filename": filename,
928 "line": lineno
929 })
930
931 def sendClearTemporaryWatch(self, condition):
932 """
933 Public method to signal the deletion of a temporary watch expression.
934
935 @param condition condition of the watch expression to be cleared
936 @type str
937 """
938 self.sendJsonCommand("ResponseClearWatch", {
939 "condition": condition,
940 })
941
942 def sendResponseLine(self, stack):
943 """
944 Public method to send the current call stack.
945
946 @param stack call stack
947 @type list
948 """
949 self.sendJsonCommand("ResponseLine", {
950 "stack": stack,
951 })
952
953 def sendCallTrace(self, event, fromInfo, toInfo):
954 """
955 Public method to send a call trace entry.
956
957 @param event trace event (call or return)
958 @type str
959 @param fromInfo dictionary containing the origin info
960 @type dict with 'filename', 'linenumber' and 'codename'
961 as keys
962 @param toInfo dictionary containing the target info
963 @type dict with 'filename', 'linenumber' and 'codename'
964 as keys
965 """
966 self.sendJsonCommand("CallTrace", {
967 "event": event[0],
968 "from": fromInfo,
969 "to": toInfo,
970 })
971
972 def sendException(self, exceptionType, exceptionMessage, stack):
973 """
974 Public method to send information for an exception.
975
976 @param exceptionType type of exception raised
977 @type str
978 @param exceptionMessage message of the exception
979 @type str
980 @param stack stack trace information
981 @type list
982 """
983 self.sendJsonCommand("ResponseException", {
984 "type": exceptionType,
985 "message": exceptionMessage,
986 "stack": stack,
987 })
988
989 def sendSyntaxError(self, message, filename, lineno, charno):
990 """
991 Public method to send information for a syntax error.
992
993 @param message syntax error message
994 @type str
995 @param filename name of the faulty file
996 @type str
997 @param lineno line number info
998 @type int
999 @param charno character number info
1000 @type int
1001 """
1002 self.sendJsonCommand("ResponseSyntax", {
1003 "message": message,
1004 "filename": filename,
1005 "linenumber": lineno,
1006 "characternumber": charno,
1007 })
1008
1009 def sendPassiveStartup(self, filename, exceptions):
1010 """
1011 Public method to send the passive start information.
1012
1013 @param filename name of the script
1014 @type str
1015 @param exceptions flag to enable exception reporting of the IDE
1016 @type bool
1017 """
1018 self.sendJsonCommand("PassiveStartup", {
1019 "filename": filename,
1020 "exceptions": exceptions,
1021 })
1022
1028 def __clientCapabilities(self): 1023 def __clientCapabilities(self):
1029 """ 1024 """
1030 Private method to determine the clients capabilities. 1025 Private method to determine the clients capabilities.
1031 1026
1032 @return client capabilities (integer) 1027 @return client capabilities (integer)
1040 return self.clientCapabilities 1035 return self.clientCapabilities
1041 except ImportError: 1036 except ImportError:
1042 return ( 1037 return (
1043 self.clientCapabilities & ~DebugClientCapabilities.HasProfiler) 1038 self.clientCapabilities & ~DebugClientCapabilities.HasProfiler)
1044 1039
1045 def write(self, s): 1040 def readReady(self, stream):
1046 """ 1041 """
1047 Public method to write data to the output stream. 1042 Public method called when there is data ready to be read.
1048 1043
1049 @param s data to be written (string) 1044 @param stream file like object that has data to be written
1050 """ 1045 """
1051 self.writestream.write(s) 1046 try:
1052 self.writestream.flush() 1047 got = stream.readline_p()
1053 1048 except Exception:
1049 return
1050
1051 if len(got) == 0:
1052 self.sessionClose()
1053 return
1054
1055 self.__receiveBuffer = self.__receiveBuffer + got
1056
1057 # Call handleLine for the line if it is complete.
1058 eol = self.__receiveBuffer.find('\n')
1059 while eol >= 0:
1060 line = self.__receiveBuffer[:eol + 1]
1061 self.__receiveBuffer = self.__receiveBuffer[eol + 1:]
1062 self.handleLine(line)
1063 eol = self.__receiveBuffer.find('\n')
1064
1065 def writeReady(self, stream):
1066 """
1067 Public method called when we are ready to write data.
1068
1069 @param stream file like object that has data to be written
1070 """
1071 stream.write_p("")
1072 stream.flush()
1073
1054 def __interact(self): 1074 def __interact(self):
1055 """ 1075 """
1056 Private method to interact with the debugger. 1076 Private method to interact with the debugger.
1057 """ 1077 """
1058 global DebugClientInstance 1078 global DebugClientInstance
1059 1079
1060 self.setDescriptors(self.readstream, self.writestream)
1061 DebugClientInstance = self 1080 DebugClientInstance = self
1081 self.__receiveBuffer = ""
1062 1082
1063 if not self.passive: 1083 if not self.passive:
1064 # At this point simulate an event loop. 1084 # At this point simulate an event loop.
1065 self.eventLoop() 1085 self.eventLoop()
1066 1086
1091 except (select.error, KeyboardInterrupt, socket.error): 1111 except (select.error, KeyboardInterrupt, socket.error):
1092 # just carry on 1112 # just carry on
1093 continue 1113 continue
1094 1114
1095 if self.readstream in rrdy: 1115 if self.readstream in rrdy:
1096 self.readReady(self.readstream.fileno()) 1116 self.readReady(self.readstream)
1097 1117
1098 if self.writestream in wrdy: 1118 if self.writestream in wrdy:
1099 self.writeReady(self.writestream.fileno()) 1119 self.writeReady(self.writestream)
1100 1120
1101 if self.errorstream in wrdy: 1121 if self.errorstream in wrdy:
1102 self.writeReady(self.errorstream.fileno()) 1122 self.writeReady(self.errorstream)
1103 1123
1104 self.eventExit = None 1124 self.eventExit = None
1105 self.pollingDisabled = False 1125 self.pollingDisabled = False
1106 1126
1107 def eventPoll(self): 1127 def eventPoll(self):
1123 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [], 0) 1143 rrdy, wrdy, xrdy = select.select([self.readstream], wrdy, [], 0)
1124 except (select.error, KeyboardInterrupt, socket.error): 1144 except (select.error, KeyboardInterrupt, socket.error):
1125 return 1145 return
1126 1146
1127 if self.readstream in rrdy: 1147 if self.readstream in rrdy:
1128 self.readReady(self.readstream.fileno()) 1148 self.readReady(self.readstream)
1129 1149
1130 if self.writestream in wrdy: 1150 if self.writestream in wrdy:
1131 self.writeReady(self.writestream.fileno()) 1151 self.writeReady(self.writestream)
1132 1152
1133 if self.errorstream in wrdy: 1153 if self.errorstream in wrdy:
1134 self.writeReady(self.errorstream.fileno()) 1154 self.writeReady(self.errorstream)
1135 1155
1136 def connectDebugger(self, port, remoteAddress=None, redirect=True): 1156 def connectDebugger(self, port, remoteAddress=None, redirect=True):
1137 """ 1157 """
1138 Public method to establish a session with the debugger. 1158 Public method to establish a session with the debugger.
1139 1159
1225 except Exception: 1245 except Exception:
1226 fargs = "" 1246 fargs = ""
1227 else: 1247 else:
1228 fargs = "" 1248 fargs = ""
1229 1249
1230 siglist = [message, [filename, linenr, ffunc, fargs]] 1250 self.sendJsonCommand("ResponseSignal", {
1231 1251 "message": message,
1232 self.write("{0}{1}".format(DebugProtocol.ResponseSignal, str(siglist))) 1252 "filename": filename,
1253 "linenumber": linenr,
1254 "function": ffunc,
1255 "arguments": fargs,
1256 })
1233 1257
1234 def absPath(self, fn): 1258 def absPath(self, fn):
1235 """ 1259 """
1236 Public method to convert a filename to an absolute name. 1260 Public method to convert a filename to an absolute name.
1237 1261
1278 1302
1279 @return flag indicating a running debug session (boolean) 1303 @return flag indicating a running debug session (boolean)
1280 """ 1304 """
1281 return self.running 1305 return self.running
1282 1306
1283 def progTerminated(self, status): 1307 def progTerminated(self, status, message=""):
1284 """ 1308 """
1285 Public method to tell the debugger that the program has terminated. 1309 Public method to tell the debugger that the program has terminated.
1286 1310
1287 @param status return status 1311 @param status return status
1288 @type int 1312 @type int
1313 @param message status message
1314 @type str
1289 """ 1315 """
1290 if status is None: 1316 if status is None:
1291 status = 0 1317 status = 0
1292 else: 1318 elif not isinstance(status, int):
1293 try: 1319 message = str(status)
1294 int(status) 1320 status = 1
1295 except ValueError:
1296 status = 1
1297 1321
1298 if self.running: 1322 if self.running:
1299 self.set_quit() 1323 self.set_quit()
1300 self.running = None 1324 self.running = None
1301 self.write('{0}{1:d}\n'.format(DebugProtocol.ResponseExit, status)) 1325 self.sendJsonCommand("ResponseExit", {
1326 "status": status,
1327 "message": message,
1328 })
1302 1329
1303 # reset coding 1330 # reset coding
1304 self.__coding = self.defaultCoding 1331 self.__coding = self.defaultCoding
1305 1332
1306 def __dumpVariables(self, frmnr, scope, filter): 1333 def __dumpVariables(self, frmnr, scope, filter):
1336 elif f.f_globals is f.f_locals: 1363 elif f.f_globals is f.f_locals:
1337 scope = -1 1364 scope = -1
1338 else: 1365 else:
1339 dict = f.f_locals 1366 dict = f.f_locals
1340 1367
1341 varlist = [scope] 1368 varlist = []
1342 1369
1343 if scope != -1: 1370 if scope != -1:
1344 keylist = dict.keys() 1371 keylist = dict.keys()
1345 1372
1346 vlist = self.__formatVariablesList(keylist, dict, scope, filter) 1373 vlist = self.__formatVariablesList(keylist, dict, scope, filter)
1347 varlist.extend(vlist) 1374 varlist.extend(vlist)
1348 1375
1349 self.write('{0}{1}\n'.format( 1376 self.sendJsonCommand("ResponseVariables", {
1350 DebugProtocol.ResponseVariables, str(varlist))) 1377 "scope": scope,
1378 "variables": varlist,
1379 })
1351 1380
1352 def __dumpVariable(self, var, frmnr, scope, filter): 1381 def __dumpVariable(self, var, frmnr, scope, filter):
1353 """ 1382 """
1354 Private method to return the variables of a frame to the debug server. 1383 Private method to return the variables of a frame to the debug server.
1355 1384
1381 elif f.f_globals is f.f_locals: 1410 elif f.f_globals is f.f_locals:
1382 scope = -1 1411 scope = -1
1383 else: 1412 else:
1384 dict = f.f_locals 1413 dict = f.f_locals
1385 1414
1386 varlist = [scope, var] 1415 varlist = []
1387 1416
1388 if scope != -1: 1417 if scope != -1:
1389 # search the correct dictionary 1418 variable = dict
1390 i = 0 1419 for attribute in var:
1391 rvar = var[:] 1420 attribute = self.__extractIndicators(attribute)[0]
1392 dictkeys = None 1421 typeObject, typeName, typeStr, resolver = \
1393 obj = None 1422 DebugVariables.getType(variable)
1394 isDict = False 1423 if resolver:
1395 formatSequences = False 1424 variable = resolver.resolve(variable, attribute)
1396 access = ""
1397 oaccess = ""
1398 odict = dict
1399
1400 qtVariable = False
1401 qvar = None
1402 qvtype = ""
1403
1404 while i < len(var):
1405 if len(dict):
1406 udict = dict
1407 ndict = {}
1408 # this has to be in line with VariablesViewer.indicators
1409 if var[i][-2:] in ["[]", "()", "{}"]: # __IGNORE_WARNING__
1410 if i + 1 == len(var):
1411 if var[i][:-2] == '...':
1412 dictkeys = [var[i - 1]]
1413 else:
1414 dictkeys = [var[i][:-2]]
1415 formatSequences = True
1416 if not access and not oaccess:
1417 if var[i][:-2] == '...':
1418 access = '["{0!s}"]'.format(var[i - 1])
1419 dict = odict
1420 else:
1421 access = '["{0!s}"]'.format(var[i][:-2])
1422 else:
1423 if var[i][:-2] == '...':
1424 if oaccess:
1425 access = oaccess
1426 else:
1427 access = '{0!s}[{1!s}]'.format(
1428 access, var[i - 1])
1429 dict = odict
1430 else:
1431 if oaccess:
1432 access = '{0!s}[{1!s}]'.format(
1433 oaccess, var[i][:-2])
1434 oaccess = ''
1435 else:
1436 access = '{0!s}[{1!s}]'.format(
1437 access, var[i][:-2])
1438 if var[i][-2:] == "{}": # __IGNORE_WARNING__
1439 isDict = True
1440 break
1441 else:
1442 if not access:
1443 if var[i][:-2] == '...':
1444 access = '["{0!s}"]'.format(var[i - 1])
1445 dict = odict
1446 else:
1447 access = '["{0!s}"]'.format(var[i][:-2])
1448 else:
1449 if var[i][:-2] == '...':
1450 access = '{0!s}[{1!s}]'.format(
1451 access, var[i - 1])
1452 dict = odict
1453 else:
1454 if oaccess:
1455 access = '{0!s}[{1!s}]'.format(
1456 oaccess, var[i][:-2])
1457 oaccess = ''
1458 else:
1459 access = '{0!s}[{1!s}]'.format(
1460 access, var[i][:-2])
1461 else: 1425 else:
1462 if access: 1426 break
1463 if oaccess: 1427 typeObject, typeName, typeStr, resolver = \
1464 access = '{0!s}[{1!s}]'.format(oaccess, var[i]) 1428 DebugVariables.getType(variable)
1465 else: 1429 if typeStr.startswith(("PyQt5.", "PyQt4.")):
1466 access = '{0!s}[{1!s}]'.format(access, var[i]) 1430 vlist = self.__formatQtVariable(variable, typeName)
1467 if var[i - 1][:-2] == '...': 1431 varlist.extend(vlist)
1468 oaccess = access 1432 elif resolver:
1469 else: 1433 dict = resolver.getDictionary(variable)
1470 oaccess = '' 1434 vlist = self.__formatVariablesList(
1471 try: 1435 list(dict.keys()), dict, scope, filter)
1472 loc = {"dict": dict} 1436 varlist.extend(vlist)
1473 exec('mdict = dict{0!s}.__dict__\nobj = dict{0!s}' 1437
1474 .format(access), globals(), loc) 1438 self.sendJsonCommand("ResponseVariable", {
1475 mdict = loc["mdict"] 1439 "scope": scope,
1476 obj = loc["obj"] 1440 "variable": var,
1477 if "PyQt4." in str(type(obj)) or \ 1441 "variables": varlist,
1478 "PyQt5." in str(type(obj)): 1442 })
1479 qtVariable = True 1443
1480 qvar = obj 1444 def __extractIndicators(self, var):
1481 qvtype = str(type(qvar))[1:-1].split()[1][1:-1] 1445 """
1482 ndict.update(mdict) 1446 Private method to extract the indicator string from a variable text.
1483 except Exception: 1447
1484 pass 1448 @param var variable text
1485 try: 1449 @type str
1486 loc = {"dict": dict} 1450 @return tuple containing the variable text without indicators and the
1487 exec('mcdict = dict{0!s}.__class__.__dict__' 1451 indicator string
1488 .format(access), globals(), loc) 1452 @rtype tuple of two str
1489 ndict.update(loc["mcdict"]) 1453 """
1490 if mdict and "sipThis" not in mdict.keys(): 1454 for indicator in DebugClientBase.Indicators:
1491 del rvar[0:2] 1455 if var.endswith(indicator):
1492 access = "" 1456 return var[:-len(indicator)], indicator
1493 except Exception: 1457
1494 pass 1458 return var, ""
1495 try: 1459
1496 loc = {"cdict": {}, "dict": dict} 1460 def __formatQtVariable(self, value, qttype):
1497 exec('slv = dict{0!s}.__slots__'.format(access),
1498 globals(), loc)
1499 for v in loc["slv"]:
1500 try:
1501 loc["v"] = v
1502 exec('cdict[v] = dict{0!s}.{1!s}'.format(
1503 access, v), globals, loc)
1504 except Exception:
1505 pass
1506 ndict.update(loc["cdict"])
1507 exec('obj = dict{0!s}'.format(access),
1508 globals(), loc)
1509 obj = loc["obj"]
1510 access = ""
1511 if "PyQt4." in str(type(obj)) or \
1512 "PyQt5." in str(type(obj)):
1513 qtVariable = True
1514 qvar = obj
1515 qvtype = str(type(qvar))[1:-1].split()[1][1:-1]
1516 except Exception:
1517 pass
1518 else:
1519 try:
1520 ndict.update(dict[var[i]].__dict__)
1521 ndict.update(dict[var[i]].__class__.__dict__)
1522 del rvar[0]
1523 obj = dict[var[i]]
1524 if "PyQt4." in str(type(obj)) or \
1525 "PyQt5." in str(type(obj)):
1526 qtVariable = True
1527 qvar = obj
1528 qvtype = str(type(qvar))[1:-1].split()[1][1:-1]
1529 except Exception:
1530 pass
1531 try:
1532 slv = dict[var[i]].__slots__
1533 loc = {"cdict": {}, "dict": dict,
1534 "var": var, "i": i}
1535 for v in slv:
1536 try:
1537 loc["v"] = v
1538 exec('cdict[v] = dict[var[i]].{0!s}'
1539 .format(v),
1540 globals(), loc)
1541 except Exception:
1542 pass
1543 ndict.update(loc["cdict"])
1544 obj = dict[var[i]]
1545 if "PyQt4." in str(type(obj)) or \
1546 "PyQt5." in str(type(obj)):
1547 qtVariable = True
1548 qvar = obj
1549 qvtype = str(type(qvar))[1:-1].split()[1][1:-1]
1550 except Exception:
1551 pass
1552 odict = dict
1553 dict = ndict
1554 i += 1
1555
1556 if qtVariable:
1557 vlist = self.__formatQtVariable(qvar, qvtype)
1558 elif ("sipThis" in dict.keys() and len(dict) == 1) or \
1559 (len(dict) == 0 and len(udict) > 0):
1560 if access:
1561 loc = {"udict": udict}
1562 exec('qvar = udict{0!s}'.format(access), globals(), loc)
1563 qvar = loc["qvar"]
1564 # this has to be in line with VariablesViewer.indicators
1565 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]: # __IGNORE_WARNING__
1566 loc = {"udict": udict}
1567 exec('qvar = udict["{0!s}"][{1!s}]'.format(rvar[0][:-2],
1568 rvar[1]),
1569 globals(), loc)
1570 qvar = loc["qvar"]
1571 else:
1572 qvar = udict[var[-1]]
1573 qvtype = str(type(qvar))[1:-1].split()[1][1:-1]
1574 if qvtype.startswith(("PyQt4", "PyQt5")):
1575 vlist = self.__formatQtVariable(qvar, qvtype)
1576 else:
1577 vlist = []
1578 else:
1579 qtVariable = False
1580 if len(dict) == 0 and len(udict) > 0:
1581 if access:
1582 loc = {"udict": udict}
1583 exec('qvar = udict{0!s}'.format(access),
1584 globals(), loc)
1585 qvar = loc["qvar"]
1586 # this has to be in line with VariablesViewer.indicators
1587 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]: # __IGNORE_WARNING__
1588 loc = {"udict": udict}
1589 exec('qvar = udict["{0!s}"][{1!s}]'.format(
1590 rvar[0][:-2], rvar[1]), globals(), loc)
1591 qvar = loc["qvar"]
1592 else:
1593 qvar = udict[var[-1]]
1594 qvtype = str(type(qvar))[1:-1].split()[1][1:-1]
1595 if qvtype.startswith(("PyQt4", "PyQt5")):
1596 qtVariable = True
1597
1598 if qtVariable:
1599 vlist = self.__formatQtVariable(qvar, qvtype)
1600 else:
1601 # format the dictionary found
1602 if dictkeys is None:
1603 dictkeys = dict.keys()
1604 else:
1605 # treatment for sequences and dictionaries
1606 if access:
1607 loc = {"dict": dict}
1608 exec("dict = dict{0!s}".format(access), globals(),
1609 loc)
1610 dict = loc["dict"]
1611 else:
1612 dict = dict[dictkeys[0]]
1613 if isDict:
1614 dictkeys = dict.keys()
1615 else:
1616 dictkeys = range(len(dict))
1617 vlist = self.__formatVariablesList(
1618 dictkeys, dict, scope, filter, formatSequences)
1619 varlist.extend(vlist)
1620
1621 if obj is not None and not formatSequences:
1622 try:
1623 if repr(obj).startswith('{'):
1624 varlist.append(
1625 ('...', 'dict', "{0:d}".format(len(obj.keys()))))
1626 elif repr(obj).startswith('['):
1627 varlist.append(
1628 ('...', 'list', "{0:d}".format(len(obj))))
1629 elif repr(obj).startswith('('):
1630 varlist.append(
1631 ('...', 'tuple', "{0:d}".format(len(obj))))
1632 except Exception:
1633 pass
1634
1635 self.write('{0}{1}\n'.format(
1636 DebugProtocol.ResponseVariable, str(varlist)))
1637
1638 def __formatQtVariable(self, value, vtype):
1639 """ 1461 """
1640 Private method to produce a formatted output of a simple Qt4/Qt5 type. 1462 Private method to produce a formatted output of a simple Qt4/Qt5 type.
1641 1463
1642 @param value variable to be formatted 1464 @param value variable to be formatted
1643 @param vtype type of the variable to be formatted (string) 1465 @param qttype type of the Qt variable to be formatted (string)
1644 @return A tuple consisting of a list of formatted variables. Each 1466 @return A tuple consisting of a list of formatted variables. Each
1645 variable entry is a tuple of three elements, the variable name, 1467 variable entry is a tuple of three elements, the variable name,
1646 its type and value. 1468 its type and value.
1647 """ 1469 """
1648 qttype = vtype.split('.')[-1]
1649 varlist = [] 1470 varlist = []
1650 if qttype == 'QChar': 1471 if qttype == 'QChar':
1651 varlist.append(("", "QChar", "{0}".format(chr(value.unicode())))) 1472 varlist.append(("", "QChar", "{0}".format(chr(value.unicode()))))
1652 varlist.append(("", "int", "{0:d}".format(value.unicode()))) 1473 varlist.append(("", "int", "{0:d}".format(value.unicode())))
1653 elif qttype == 'QByteArray': 1474 elif qttype == 'QByteArray':
1816 break 1637 break
1817 if matched: 1638 if matched:
1818 continue 1639 continue
1819 1640
1820 # filter hidden attributes (filter #0) 1641 # filter hidden attributes (filter #0)
1821 if 0 in filter and str(key)[:2] == '__': 1642 if 0 in filter and str(key)[:2] == '__' and not (
1643 key == "___len___" and
1644 DebugVariables.TooLargeAttribute in keylist):
1822 continue 1645 continue
1823 1646
1824 # special handling for '__builtins__' (it's way too big) 1647 # special handling for '__builtins__' (it's way too big)
1825 if key == '__builtins__': 1648 if key == '__builtins__':
1826 rvalue = '<module __builtin__ (built-in)>' 1649 rvalue = '<module __builtin__ (built-in)>'
1833 if valtype not in ConfigVarTypeStrings: 1656 if valtype not in ConfigVarTypeStrings:
1834 if ConfigVarTypeStrings.index('instance') in filter: 1657 if ConfigVarTypeStrings.index('instance') in filter:
1835 continue 1658 continue
1836 elif valtype == "sip.methoddescriptor": 1659 elif valtype == "sip.methoddescriptor":
1837 if ConfigVarTypeStrings.index( 1660 if ConfigVarTypeStrings.index(
1838 'instance method') in filter: 1661 'method') in filter:
1839 continue 1662 continue
1840 elif valtype == "sip.enumtype": 1663 elif valtype == "sip.enumtype":
1841 if ConfigVarTypeStrings.index('class') in filter: 1664 if ConfigVarTypeStrings.index('class') in filter:
1842 continue 1665 continue
1843 valtype = valtypestr 1666 valtype = valtypestr
1850 if ConfigVarTypeStrings.index( 1673 if ConfigVarTypeStrings.index(
1851 'instance') in filter: 1674 'instance') in filter:
1852 continue 1675 continue
1853 elif valtype == "sip.methoddescriptor": 1676 elif valtype == "sip.methoddescriptor":
1854 if ConfigVarTypeStrings.index( 1677 if ConfigVarTypeStrings.index(
1855 'instance method') in filter: 1678 'method') in filter:
1856 continue 1679 continue
1857 elif valtype == "sip.enumtype": 1680 elif valtype == "sip.enumtype":
1858 if ConfigVarTypeStrings.index('class') in filter: 1681 if ConfigVarTypeStrings.index('class') in filter:
1859 continue 1682 continue
1860 elif not valtype.startswith("PySide") and \ 1683 elif not valtype.startswith("PySide") and \
1861 ConfigVarTypeStrings.index('other') in filter: 1684 ConfigVarTypeStrings.index('other') in filter:
1862 continue 1685 continue
1863 1686
1864 try: 1687 try:
1865 if valtype not in ['list', 'tuple', 'dict']: 1688 if valtype not in ['list', 'tuple', 'dict', 'set',
1689 'frozenset']:
1866 rvalue = repr(value) 1690 rvalue = repr(value)
1867 if valtype.startswith('class') and \ 1691 if valtype.startswith('class') and \
1868 rvalue[0] in ['{', '(', '[']: 1692 rvalue[0] in ['{', '(', '[']:
1869 rvalue = "" 1693 rvalue = ""
1870 else: 1694 else:
1927 self.__getCompletionList(text, localCompleter, completions) 1751 self.__getCompletionList(text, localCompleter, completions)
1928 except AttributeError: 1752 except AttributeError:
1929 pass 1753 pass
1930 self.__getCompletionList(text, self.complete, completions) 1754 self.__getCompletionList(text, self.complete, completions)
1931 1755
1932 self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion, 1756 self.sendJsonCommand("ResponseCompletion", {
1933 str(list(completions)), text)) 1757 "completions": list(completions),
1758 "text": text,
1759 })
1934 1760
1935 def __getCompletionList(self, text, completer, completions): 1761 def __getCompletionList(self, text, completer, completions):
1936 """ 1762 """
1937 Private method to create a completions list. 1763 Private method to create a completions list.
1938 1764
1986 except IndexError: 1812 except IndexError:
1987 self.running = None 1813 self.running = None
1988 if self.running: 1814 if self.running:
1989 self.__setCoding(self.running) 1815 self.__setCoding(self.running)
1990 self.passive = True 1816 self.passive = True
1991 self.write("{0}{1}|{2:d}\n".format(DebugProtocol.PassiveStartup, 1817 self.sendPassiveStartup(self.running, exceptions)
1992 self.running, exceptions))
1993 self.__interact() 1818 self.__interact()
1994 1819
1995 # setup the debugger variables 1820 # setup the debugger variables
1996 self._fncache = {} 1821 self._fncache = {}
1997 self.dircache = [] 1822 self.dircache = []
1998 self.mainFrame = None 1823 self.mainFrame = None
1999 self.inRawMode = False
2000 self.debugging = True 1824 self.debugging = True
2001 1825
2002 self.attachThread(mainThread=True) 1826 self.attachThread(mainThread=True)
2003 self.mainThread.tracePythonLibs(tracePython) 1827 self.mainThread.tracePythonLibs(tracePython)
2004 1828
2047 else: 1871 else:
2048 os.chdir(wd) 1872 os.chdir(wd)
2049 self.running = sys.argv[0] 1873 self.running = sys.argv[0]
2050 self.__setCoding(self.running) 1874 self.__setCoding(self.running)
2051 self.mainFrame = None 1875 self.mainFrame = None
2052 self.inRawMode = False
2053 self.debugging = True 1876 self.debugging = True
2054 1877
2055 self.passive = True 1878 self.passive = True
2056 self.write("{0}{1}|{2:d}\n".format( 1879 self.sendPassiveStartup(self.running, exceptions)
2057 DebugProtocol.PassiveStartup, self.running, exceptions))
2058 self.__interact() 1880 self.__interact()
2059 1881
2060 self.attachThread(mainThread=True) 1882 self.attachThread(mainThread=True)
2061 self.mainThread.tracePythonLibs(tracePython) 1883 self.mainThread.tracePythonLibs(tracePython)
2062 1884
2209 to follow. 2031 to follow.
2210 2032
2211 @return process ID (integer) 2033 @return process ID (integer)
2212 """ 2034 """
2213 if not self.fork_auto: 2035 if not self.fork_auto:
2214 self.write(DebugProtocol.RequestForkTo + '\n') 2036 self.sendJsonCommand("RequestForkTo", {})
2215 self.eventLoop(True) 2037 self.eventLoop(True)
2216 pid = DebugClientOrigFork() 2038 pid = DebugClientOrigFork()
2217 if pid == 0: 2039 if pid == 0:
2218 # child 2040 # child
2219 if not self.fork_child: 2041 if not self.fork_child:

eric ide

mercurial