92 # use our own close(). |
92 # use our own close(). |
93 if 'close' in dir(os): |
93 if 'close' in dir(os): |
94 DebugClientOrigClose = os.close |
94 DebugClientOrigClose = os.close |
95 os.close = DebugClientClose |
95 os.close = DebugClientClose |
96 |
96 |
97 ################################################################################ |
97 ############################################################################### |
98 |
98 |
99 |
99 |
100 def DebugClientSetRecursionLimit(limit): |
100 def DebugClientSetRecursionLimit(limit): |
101 """ |
101 """ |
102 Replacement for the standard sys.setrecursionlimit(limit). |
102 Replacement for the standard sys.setrecursionlimit(limit). |
111 if 'setrecursionlimit' in dir(sys): |
111 if 'setrecursionlimit' in dir(sys): |
112 DebugClientOrigSetRecursionLimit = sys.setrecursionlimit |
112 DebugClientOrigSetRecursionLimit = sys.setrecursionlimit |
113 sys.setrecursionlimit = DebugClientSetRecursionLimit |
113 sys.setrecursionlimit = DebugClientSetRecursionLimit |
114 DebugClientSetRecursionLimit(sys.getrecursionlimit()) |
114 DebugClientSetRecursionLimit(sys.getrecursionlimit()) |
115 |
115 |
116 ################################################################################ |
116 ############################################################################### |
117 |
117 |
118 |
118 |
119 class DebugClientBase(object): |
119 class DebugClientBase(object): |
120 """ |
120 """ |
121 Class implementing the client side of the debugger. |
121 Class implementing the client side of the debugger. |
122 |
122 |
123 It provides access to the Python interpeter from a debugger running in another |
123 It provides access to the Python interpeter from a debugger running in |
124 process whether or not the Qt event loop is running. |
124 another process whether or not the Qt event loop is running. |
125 |
125 |
126 The protocol between the debugger and the client assumes that there will be |
126 The protocol between the debugger and the client assumes that there will be |
127 a single source of debugger commands and a single source of Python |
127 a single source of debugger commands and a single source of Python |
128 statements. Commands and statement are always exactly one line and may be |
128 statements. Commands and statement are always exactly one line and may be |
129 interspersed. |
129 interspersed. |
132 debugger and then sends a series of one line commands. A command is either |
132 debugger and then sends a series of one line commands. A command is either |
133 >Load<, >Step<, >StepInto<, ... or a Python statement. |
133 >Load<, >Step<, >StepInto<, ... or a Python statement. |
134 See DebugProtocol.py for a listing of valid protocol tokens. |
134 See DebugProtocol.py for a listing of valid protocol tokens. |
135 |
135 |
136 A Python statement consists of the statement to execute, followed (in a |
136 A Python statement consists of the statement to execute, followed (in a |
137 separate line) by >OK?<. If the statement was incomplete then the response |
137 separate line) by >OK?<. If the statement was incomplete then the |
138 is >Continue<. If there was an exception then the response is |
138 response is >Continue<. If there was an exception then the response |
139 >Exception<. |
139 is >Exception<. Otherwise the response is >OK<. The reason |
140 Otherwise the response is >OK<. The reason for the >OK?< part is to |
140 for the >OK?< part is to provide a sentinal (ie. the responding |
141 provide a sentinal (ie. the responding >OK<) after any possible output as a |
141 >OK<) after any possible output as a result of executing the command. |
142 result of executing the command. |
|
143 |
142 |
144 The client may send any other lines at any other time which should be |
143 The client may send any other lines at any other time which should be |
145 interpreted as program output. |
144 interpreted as program output. |
146 |
145 |
147 If the debugger closes the session there is no response from the client. |
146 If the debugger closes the session there is no response from the client. |
148 The client may close the session at any time as a result of the script |
147 The client may close the session at any time as a result of the script |
149 being debugged closing or crashing. |
148 being debugged closing or crashing. |
150 |
149 |
151 <b>Note</b>: This class is meant to be subclassed by individual DebugClient classes. |
150 <b>Note</b>: This class is meant to be subclassed by individual |
152 Do not instantiate it directly. |
151 DebugClient classes. Do not instantiate it directly. |
153 """ |
152 """ |
154 clientCapabilities = DebugClientCapabilities.HasAll |
153 clientCapabilities = DebugClientCapabilities.HasAll |
155 |
154 |
156 def __init__(self): |
155 def __init__(self): |
157 """ |
156 """ |
254 if m: |
254 if m: |
255 self.__coding = m.group(1) |
255 self.__coding = m.group(1) |
256 return |
256 return |
257 self.__coding = default |
257 self.__coding = default |
258 |
258 |
259 def attachThread(self, target=None, args=None, kwargs=None, mainThread=False): |
259 def attachThread(self, target=None, args=None, kwargs=None, |
|
260 mainThread=False): |
260 """ |
261 """ |
261 Public method to setup a thread for DebugClient to debug. |
262 Public method to setup a thread for DebugClient to debug. |
262 |
263 |
263 If mainThread is non-zero, then we are attaching to the already |
264 If mainThread is non-zero, then we are attaching to the already |
264 started mainthread of the app and the rest of the args are ignored. |
265 started mainthread of the app and the rest of the args are ignored. |
265 |
266 |
266 @param target the start function of the target thread (i.e. the user code) |
267 @param target the start function of the target thread (i.e. the |
|
268 user code) |
267 @param args arguments to pass to target |
269 @param args arguments to pass to target |
268 @param kwargs keyword arguments to pass to target |
270 @param kwargs keyword arguments to pass to target |
269 @param mainThread True, if we are attaching to the already |
271 @param mainThread True, if we are attaching to the already |
270 started mainthread of the app |
272 started mainthread of the app |
271 """ |
273 """ |
275 def __dumpThreadList(self): |
277 def __dumpThreadList(self): |
276 """ |
278 """ |
277 Public method to send the list of threads. |
279 Public method to send the list of threads. |
278 """ |
280 """ |
279 threadList = [] |
281 threadList = [] |
280 if self.threads and self.currentThread: # indication for the threaded debugger |
282 if self.threads and self.currentThread: |
|
283 # indication for the threaded debugger |
281 currentId = self.currentThread.get_ident() |
284 currentId = self.currentThread.get_ident() |
282 for t in self.threads.values(): |
285 for t in self.threads.values(): |
283 d = {} |
286 d = {} |
284 d["id"] = t.get_ident() |
287 d["id"] = t.get_ident() |
285 d["name"] = t.get_name() |
288 d["name"] = t.get_name() |
302 |
305 |
303 @param prompt the prompt to be shown (string) |
306 @param prompt the prompt to be shown (string) |
304 @param echo Flag indicating echoing of the input (boolean) |
307 @param echo Flag indicating echoing of the input (boolean) |
305 @return the entered string |
308 @return the entered string |
306 """ |
309 """ |
307 self.write("{0}{1!r}\n".format(DebugProtocol.ResponseRaw, (prompt, echo))) |
310 self.write("{0}{1!r}\n".format( |
|
311 DebugProtocol.ResponseRaw, (prompt, echo))) |
308 self.inRawMode = True |
312 self.inRawMode = True |
309 self.eventLoop(True) |
313 self.eventLoop(True) |
310 return self.rawLine |
314 return self.rawLine |
311 |
315 |
312 def __exceptionRaised(self): |
316 def __exceptionRaised(self): |
357 try: |
362 try: |
358 code = compile(statement + '\n', filename, mode) |
363 code = compile(statement + '\n', filename, mode) |
359 except SyntaxError: |
364 except SyntaxError: |
360 exctype, excval, exctb = sys.exc_info() |
365 exctype, excval, exctb = sys.exc_info() |
361 try: |
366 try: |
362 message, (filename, linenr, charnr, text) = excval[0], excval[1] |
367 message, (filename, linenr, charnr, text) = \ |
|
368 excval[0], excval[1] |
363 except ValueError: |
369 except ValueError: |
364 exclist = [] |
370 exclist = [] |
365 else: |
371 else: |
366 exclist = [message, [filename, linenr, charnr]] |
372 exclist = [message, [filename, linenr, charnr]] |
367 |
373 |
368 self.write("{0}{1}\n".format(DebugProtocol.ResponseSyntax, str(exclist))) |
374 self.write("{0}{1}\n".format( |
|
375 DebugProtocol.ResponseSyntax, str(exclist))) |
369 return None |
376 return None |
370 |
377 |
371 return code |
378 return code |
372 |
379 |
373 def handleLine(self, line): |
380 def handleLine(self, line): |
374 """ |
381 """ |
375 Public method to handle the receipt of a complete line. |
382 Public method to handle the receipt of a complete line. |
376 |
383 |
377 It first looks for a valid protocol token at the start of the line. Thereafter |
384 It first looks for a valid protocol token at the start of the line. |
378 it trys to execute the lines accumulated so far. |
385 Thereafter it trys to execute the lines accumulated so far. |
379 |
386 |
380 @param line the received line |
387 @param line the received line |
381 """ |
388 """ |
382 # Remove any newline. |
389 # Remove any newline. |
383 if line[-1] == '\n': |
390 if line[-1] == '\n': |
410 tid = eval(arg) |
417 tid = eval(arg) |
411 if tid in self.threads: |
418 if tid in self.threads: |
412 self.setCurrentThread(tid) |
419 self.setCurrentThread(tid) |
413 self.write(DebugProtocol.ResponseThreadSet + '\n') |
420 self.write(DebugProtocol.ResponseThreadSet + '\n') |
414 stack = self.currentThread.getStack() |
421 stack = self.currentThread.getStack() |
415 self.write('{0}{1!r}\n'.format(DebugProtocol.ResponseStack, stack)) |
422 self.write('{0}{1!r}\n'.format( |
|
423 DebugProtocol.ResponseStack, stack)) |
416 return |
424 return |
417 |
425 |
418 if cmd == DebugProtocol.RequestStep: |
426 if cmd == DebugProtocol.RequestStep: |
419 self.currentThread.step(True) |
427 self.currentThread.step(True) |
420 self.eventExit = True |
428 self.eventExit = True |
496 |
505 |
497 # set the system exception handling function to ensure, that |
506 # set the system exception handling function to ensure, that |
498 # we report on all unhandled exceptions |
507 # we report on all unhandled exceptions |
499 sys.excepthook = self.__unhandled_exception |
508 sys.excepthook = self.__unhandled_exception |
500 |
509 |
501 # clear all old breakpoints, they'll get set after we have started |
510 # clear all old breakpoints, they'll get set after we have |
|
511 # started |
502 self.mainThread.clear_all_breaks() |
512 self.mainThread.clear_all_breaks() |
503 |
513 |
504 self.mainThread.tracePython = tracePython |
514 self.mainThread.tracePython = tracePython |
505 |
515 |
506 # This will eventually enter a local event loop. |
516 # This will eventually enter a local event loop. |
507 # Note the use of backquotes to cause a repr of self.running. The |
517 # Note the use of backquotes to cause a repr of self.running. |
508 # need for this is on Windows os where backslash is the path separator. |
518 # The need for this is on Windows os where backslash is the |
509 # They will get inadvertantly stripped away during the eval causing |
519 # path separator. They will get inadvertantly stripped away |
510 # IOErrors, if self.running is passed as a normal str. |
520 # during the eval causing IOErrors, if self.running is passed |
|
521 # as a normal str. |
511 self.debugMod.__dict__['__file__'] = self.running |
522 self.debugMod.__dict__['__file__'] = self.running |
512 sys.modules['__main__'] = self.debugMod |
523 sys.modules['__main__'] = self.debugMod |
513 code = self.__compileFileSource(self.running) |
524 code = self.__compileFileSource(self.running) |
514 if code: |
525 if code: |
515 self.callTraceEnabled = self.__newCallTraceEnabled |
526 self.callTraceEnabled = self.__newCallTraceEnabled |
618 # we report on all unhandled exceptions |
629 # we report on all unhandled exceptions |
619 sys.excepthook = self.__unhandled_exception |
630 sys.excepthook = self.__unhandled_exception |
620 |
631 |
621 # generate a coverage object |
632 # generate a coverage object |
622 self.cover = coverage(auto_data=True, |
633 self.cover = coverage(auto_data=True, |
623 data_file="{0}.coverage".format(os.path.splitext(sys.argv[0])[0])) |
634 data_file="{0}.coverage".format( |
|
635 os.path.splitext(sys.argv[0])[0])) |
624 self.cover.use_cache(True) |
636 self.cover.use_cache(True) |
625 |
637 |
626 if int(erase): |
638 if int(erase): |
627 self.cover.erase() |
639 self.cover.erase() |
628 sys.modules['__main__'] = self.debugMod |
640 sys.modules['__main__'] = self.debugMod |
666 else: |
678 else: |
667 try: |
679 try: |
668 compile(cond, '<string>', 'eval') |
680 compile(cond, '<string>', 'eval') |
669 except SyntaxError: |
681 except SyntaxError: |
670 self.write('{0}{1},{2:d}\n'.format( |
682 self.write('{0}{1},{2:d}\n'.format( |
671 DebugProtocol.ResponseBPConditionError, fn, line)) |
683 DebugProtocol.ResponseBPConditionError, |
|
684 fn, line)) |
672 return |
685 return |
673 self.mainThread.set_break(fn, line, temporary, cond) |
686 self.mainThread.set_break(fn, line, temporary, cond) |
674 else: |
687 else: |
675 self.mainThread.clear_break(fn, line) |
688 self.mainThread.clear_break(fn, line) |
676 |
689 |
850 except AttributeError: |
865 except AttributeError: |
851 self.test = unittest.defaultTestLoader\ |
866 self.test = unittest.defaultTestLoader\ |
852 .loadTestsFromModule(utModule) |
867 .loadTestsFromModule(utModule) |
853 except: |
868 except: |
854 exc_type, exc_value, exc_tb = sys.exc_info() |
869 exc_type, exc_value, exc_tb = sys.exc_info() |
855 self.write('{0}{1}\n'.format(DebugProtocol.ResponseUTPrepared, |
870 self.write('{0}{1}\n'.format( |
|
871 DebugProtocol.ResponseUTPrepared, |
856 str((0, str(exc_type), str(exc_value))))) |
872 str((0, str(exc_type), str(exc_value))))) |
857 self.__exceptionRaised() |
873 self.__exceptionRaised() |
858 return |
874 return |
859 |
875 |
860 # generate a coverage object |
876 # generate a coverage object |
861 if int(cov): |
877 if int(cov): |
862 from coverage import coverage |
878 from coverage import coverage |
863 self.cover = coverage(auto_data=True, |
879 self.cover = coverage(auto_data=True, |
864 data_file="{0}.coverage".format(os.path.splitext(covname)[0])) |
880 data_file="{0}.coverage".format( |
|
881 os.path.splitext(covname)[0])) |
865 self.cover.use_cache(True) |
882 self.cover.use_cache(True) |
866 if int(erase): |
883 if int(erase): |
867 self.cover.erase() |
884 self.cover.erase() |
868 else: |
885 else: |
869 self.cover = None |
886 self.cover = None |
914 try: |
931 try: |
915 code = self.compile_command(self.buffer, self.readstream.name) |
932 code = self.compile_command(self.buffer, self.readstream.name) |
916 except (OverflowError, SyntaxError, ValueError): |
933 except (OverflowError, SyntaxError, ValueError): |
917 # Report the exception |
934 # Report the exception |
918 sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() |
935 sys.last_type, sys.last_value, sys.last_traceback = sys.exc_info() |
919 for l in traceback.format_exception_only(sys.last_type, sys.last_value): |
936 for l in traceback.format_exception_only( |
|
937 sys.last_type, sys.last_value): |
920 self.write(l) |
938 self.write(l) |
921 self.buffer = '' |
939 self.buffer = '' |
922 else: |
940 else: |
923 if code is None: |
941 if code is None: |
924 self.pendingResponse = DebugProtocol.ResponseContinue |
942 self.pendingResponse = DebugProtocol.ResponseContinue |
945 frmnr = self.framenr |
963 frmnr = self.framenr |
946 while cf is not None and frmnr > 0: |
964 while cf is not None and frmnr > 0: |
947 cf = cf.f_back |
965 cf = cf.f_back |
948 frmnr -= 1 |
966 frmnr -= 1 |
949 _globals = cf.f_globals |
967 _globals = cf.f_globals |
950 _locals = self.currentThread.getCurrentFrameLocals() |
968 _locals = \ |
|
969 self.currentThread.getCurrentFrameLocals() |
951 # reset sys.stdout to our redirector (unconditionally) |
970 # reset sys.stdout to our redirector (unconditionally) |
952 if "sys" in _globals: |
971 if "sys" in _globals: |
953 __stdout = _globals["sys"].stdout |
972 __stdout = _globals["sys"].stdout |
954 _globals["sys"].stdout = self.writestream |
973 _globals["sys"].stdout = self.writestream |
955 exec(code, _globals, _locals) |
974 exec(code, _globals, _locals) |
1103 It opens a network connection to the debugger, connects it to stdin, |
1123 It opens a network connection to the debugger, connects it to stdin, |
1104 stdout and stderr and saves these file objects in case the application |
1124 stdout and stderr and saves these file objects in case the application |
1105 being debugged redirects them itself. |
1125 being debugged redirects them itself. |
1106 |
1126 |
1107 @param port the port number to connect to (int) |
1127 @param port the port number to connect to (int) |
1108 @param remoteAddress the network address of the debug server host (string) |
1128 @param remoteAddress the network address of the debug server host |
1109 @param redirect flag indicating redirection of stdin, stdout and stderr (boolean) |
1129 (string) |
1110 """ |
1130 @param redirect flag indicating redirection of stdin, stdout and |
1111 if remoteAddress is None: # default: 127.0.0.1 |
1131 stderr (boolean) |
|
1132 """ |
|
1133 if remoteAddress is None: # default: 127.0.0.1 |
1112 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
1134 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
1113 sock.connect((DebugProtocol.DebugAddress, port)) |
1135 sock.connect((DebugProtocol.DebugAddress, port)) |
1114 else: |
1136 else: |
1115 if "@@i" in remoteAddress: |
1137 if "@@i" in remoteAddress: |
1116 remoteAddress, index = remoteAddress.split("@@i") |
1138 remoteAddress, index = remoteAddress.split("@@i") |
1117 else: |
1139 else: |
1118 index = 0 |
1140 index = 0 |
1119 if ":" in remoteAddress: # IPv6 |
1141 if ":" in remoteAddress: # IPv6 |
1120 sockaddr = socket.getaddrinfo( |
1142 sockaddr = socket.getaddrinfo( |
1121 remoteAddress, port, 0, 0, socket.SOL_TCP)[0][-1] |
1143 remoteAddress, port, 0, 0, socket.SOL_TCP)[0][-1] |
1122 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) |
1144 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) |
1123 sockaddr = sockaddr[:-1] + (int(index),) |
1145 sockaddr = sockaddr[:-1] + (int(index),) |
1124 sock.connect(sockaddr) |
1146 sock.connect(sockaddr) |
1125 else: # IPv4 |
1147 else: # IPv4 |
1126 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
1148 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) |
1127 sock.connect((remoteAddress, port)) |
1149 sock.connect((remoteAddress, port)) |
1128 |
1150 |
1129 self.readstream = AsyncFile(sock, sys.stdin.mode, sys.stdin.name) |
1151 self.readstream = AsyncFile(sock, sys.stdin.mode, sys.stdin.name) |
1130 self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name) |
1152 self.writestream = AsyncFile(sock, sys.stdout.mode, sys.stdout.name) |
1245 |
1268 |
1246 def __dumpVariables(self, frmnr, scope, filter): |
1269 def __dumpVariables(self, frmnr, scope, filter): |
1247 """ |
1270 """ |
1248 Private method to return the variables of a frame to the debug server. |
1271 Private method to return the variables of a frame to the debug server. |
1249 |
1272 |
1250 @param frmnr distance of frame reported on. 0 is the current frame (int) |
1273 @param frmnr distance of frame reported on. 0 is the current frame |
|
1274 (int) |
1251 @param scope 1 to report global variables, 0 for local variables (int) |
1275 @param scope 1 to report global variables, 0 for local variables (int) |
1252 @param filter the indices of variable types to be filtered (list of int) |
1276 @param filter the indices of variable types to be filtered |
|
1277 (list of int) |
1253 """ |
1278 """ |
1254 if self.currentThread is None: |
1279 if self.currentThread is None: |
1255 return |
1280 return |
1256 |
1281 |
1257 if scope == 0: |
1282 if scope == 0: |
1280 keylist = dict.keys() |
1305 keylist = dict.keys() |
1281 |
1306 |
1282 vlist = self.__formatVariablesList(keylist, dict, scope, filter) |
1307 vlist = self.__formatVariablesList(keylist, dict, scope, filter) |
1283 varlist.extend(vlist) |
1308 varlist.extend(vlist) |
1284 |
1309 |
1285 self.write('{0}{1}\n'.format(DebugProtocol.ResponseVariables, str(varlist))) |
1310 self.write('{0}{1}\n'.format( |
|
1311 DebugProtocol.ResponseVariables, str(varlist))) |
1286 |
1312 |
1287 def __dumpVariable(self, var, frmnr, scope, filter): |
1313 def __dumpVariable(self, var, frmnr, scope, filter): |
1288 """ |
1314 """ |
1289 Private method to return the variables of a frame to the debug server. |
1315 Private method to return the variables of a frame to the debug server. |
1290 |
1316 |
1291 @param var list encoded name of the requested variable (list of strings) |
1317 @param var list encoded name of the requested variable |
1292 @param frmnr distance of frame reported on. 0 is the current frame (int) |
1318 (list of strings) |
|
1319 @param frmnr distance of frame reported on. 0 is the current frame |
|
1320 (int) |
1293 @param scope 1 to report global variables, 0 for local variables (int) |
1321 @param scope 1 to report global variables, 0 for local variables (int) |
1294 @param filter the indices of variable types to be filtered (list of int) |
1322 @param filter the indices of variable types to be filtered |
|
1323 (list of int) |
1295 """ |
1324 """ |
1296 if self.currentThread is None: |
1325 if self.currentThread is None: |
1297 return |
1326 return |
1298 |
1327 |
1299 f = self.currentThread.getCurrentFrame() |
1328 f = self.currentThread.getCurrentFrame() |
1372 dict = odict |
1404 dict = odict |
1373 else: |
1405 else: |
1374 access = '["{0!s}"]'.format(var[i][:-2]) |
1406 access = '["{0!s}"]'.format(var[i][:-2]) |
1375 else: |
1407 else: |
1376 if var[i][:-2] == '...': |
1408 if var[i][:-2] == '...': |
1377 access = '{0!s}[{1!s}]'.format(access, var[i - 1]) |
1409 access = '{0!s}[{1!s}]'.format( |
|
1410 access, var[i - 1]) |
1378 dict = odict |
1411 dict = odict |
1379 else: |
1412 else: |
1380 if oaccess: |
1413 if oaccess: |
1381 access = '{0!s}[{1!s}]'.format(oaccess, var[i][:-2]) |
1414 access = '{0!s}[{1!s}]'.format( |
|
1415 oaccess, var[i][:-2]) |
1382 oaccess = '' |
1416 oaccess = '' |
1383 else: |
1417 else: |
1384 access = '{0!s}[{1!s}]'.format(access, var[i][:-2]) |
1418 access = '{0!s}[{1!s}]'.format( |
|
1419 access, var[i][:-2]) |
1385 else: |
1420 else: |
1386 if access: |
1421 if access: |
1387 if oaccess: |
1422 if oaccess: |
1388 access = '{0!s}[{1!s}]'.format(oaccess, var[i]) |
1423 access = '{0!s}[{1!s}]'.format(oaccess, var[i]) |
1389 else: |
1424 else: |
1479 exec('qvar = udict{0!s}'.format(access), globals(), loc) |
1518 exec('qvar = udict{0!s}'.format(access), globals(), loc) |
1480 qvar = loc["qvar"] |
1519 qvar = loc["qvar"] |
1481 # this has to be in line with VariablesViewer.indicators |
1520 # this has to be in line with VariablesViewer.indicators |
1482 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]: |
1521 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]: |
1483 loc = {"udict": udict} |
1522 loc = {"udict": udict} |
1484 exec('qvar = udict["{0!s}"][{1!s}]'.format(rvar[0][:-2], rvar[1]), |
1523 exec('qvar = udict["{0!s}"][{1!s}]'.format(rvar[0][:-2], |
|
1524 rvar[1]), |
1485 globals(), loc) |
1525 globals(), loc) |
1486 qvar = loc["qvar"] |
1526 qvar = loc["qvar"] |
1487 else: |
1527 else: |
1488 qvar = udict[var[-1]] |
1528 qvar = udict[var[-1]] |
1489 qvtype = str(type(qvar))[1:-1].split()[1][1:-1] |
1529 qvtype = str(type(qvar))[1:-1].split()[1][1:-1] |
1499 exec('qvar = udict{0!s}'.format(access), globals(), loc) |
1539 exec('qvar = udict{0!s}'.format(access), globals(), loc) |
1500 qvar = loc["qvar"] |
1540 qvar = loc["qvar"] |
1501 # this has to be in line with VariablesViewer.indicators |
1541 # this has to be in line with VariablesViewer.indicators |
1502 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]: |
1542 elif rvar and rvar[0][-2:] in ["[]", "()", "{}"]: |
1503 loc = {"udict": udict} |
1543 loc = {"udict": udict} |
1504 exec('qvar = udict["{0!s}"][{1!s}]'.format(rvar[0][:-2], rvar[1]), |
1544 exec('qvar = udict["{0!s}"][{1!s}]'.format( |
1505 globals(), loc) |
1545 rvar[0][:-2], rvar[1]), globals(), loc) |
1506 qvar = loc["qvar"] |
1546 qvar = loc["qvar"] |
1507 else: |
1547 else: |
1508 qvar = udict[var[-1]] |
1548 qvar = udict[var[-1]] |
1509 qvtype = str(type(qvar))[1:-1].split()[1][1:-1] |
1549 qvtype = str(type(qvar))[1:-1].split()[1][1:-1] |
1510 if qvtype.startswith("PyQt4"): |
1550 if qvtype.startswith("PyQt4"): |
1518 dictkeys = dict.keys() |
1558 dictkeys = dict.keys() |
1519 else: |
1559 else: |
1520 # treatment for sequences and dictionaries |
1560 # treatment for sequences and dictionaries |
1521 if access: |
1561 if access: |
1522 loc = {"dict": dict} |
1562 loc = {"dict": dict} |
1523 exec("dict = dict{0!s}".format(access), globals(), loc) |
1563 exec("dict = dict{0!s}".format(access), globals(), |
|
1564 loc) |
1524 dict = loc["dict"] |
1565 dict = loc["dict"] |
1525 else: |
1566 else: |
1526 dict = dict[dictkeys[0]] |
1567 dict = dict[dictkeys[0]] |
1527 if isDict: |
1568 if isDict: |
1528 dictkeys = dict.keys() |
1569 dictkeys = dict.keys() |
1529 else: |
1570 else: |
1530 dictkeys = range(len(dict)) |
1571 dictkeys = range(len(dict)) |
1531 vlist = self.__formatVariablesList(dictkeys, dict, scope, filter, |
1572 vlist = self.__formatVariablesList( |
1532 formatSequences) |
1573 dictkeys, dict, scope, filter, formatSequences) |
1533 varlist.extend(vlist) |
1574 varlist.extend(vlist) |
1534 |
1575 |
1535 if obj is not None and not formatSequences: |
1576 if obj is not None and not formatSequences: |
1536 try: |
1577 try: |
1537 if repr(obj).startswith('{'): |
1578 if repr(obj).startswith('{'): |
1538 varlist.append(('...', 'dict', "{0:d}".format(len(obj.keys())))) |
1579 varlist.append( |
|
1580 ('...', 'dict', "{0:d}".format(len(obj.keys())))) |
1539 elif repr(obj).startswith('['): |
1581 elif repr(obj).startswith('['): |
1540 varlist.append(('...', 'list', "{0:d}".format(len(obj)))) |
1582 varlist.append( |
|
1583 ('...', 'list', "{0:d}".format(len(obj)))) |
1541 elif repr(obj).startswith('('): |
1584 elif repr(obj).startswith('('): |
1542 varlist.append(('...', 'tuple', "{0:d}".format(len(obj)))) |
1585 varlist.append( |
|
1586 ('...', 'tuple', "{0:d}".format(len(obj)))) |
1543 except: |
1587 except: |
1544 pass |
1588 pass |
1545 |
1589 |
1546 self.write('{0}{1}\n'.format(DebugProtocol.ResponseVariable, str(varlist))) |
1590 self.write('{0}{1}\n'.format( |
|
1591 DebugProtocol.ResponseVariable, str(varlist))) |
1547 |
1592 |
1548 def __formatQt4Variable(self, value, vtype): |
1593 def __formatQt4Variable(self, value, vtype): |
1549 """ |
1594 """ |
1550 Private method to produce a formatted output of a simple Qt4/Qt5 type. |
1595 Private method to produce a formatted output of a simple Qt4/Qt5 type. |
1551 |
1596 |
1559 varlist = [] |
1604 varlist = [] |
1560 if qttype == 'QChar': |
1605 if qttype == 'QChar': |
1561 varlist.append(("", "QChar", "{0}".format(chr(value.unicode())))) |
1606 varlist.append(("", "QChar", "{0}".format(chr(value.unicode())))) |
1562 varlist.append(("", "int", "{0:d}".format(value.unicode()))) |
1607 varlist.append(("", "int", "{0:d}".format(value.unicode()))) |
1563 elif qttype == 'QByteArray': |
1608 elif qttype == 'QByteArray': |
1564 varlist.append(("bytes", "QByteArray", "{0}".format(bytes(value))[2:-1])) |
1609 varlist.append( |
1565 varlist.append(("hex", "QByteArray", "{0}".format(value.toHex())[2:-1])) |
1610 ("bytes", "QByteArray", "{0}".format(bytes(value))[2:-1])) |
1566 varlist.append(("base64", "QByteArray", "{0}".format(value.toBase64())[2:-1])) |
1611 varlist.append( |
|
1612 ("hex", "QByteArray", "{0}".format(value.toHex())[2:-1])) |
|
1613 varlist.append( |
|
1614 ("base64", "QByteArray", "{0}".format(value.toBase64())[2:-1])) |
1567 varlist.append(("percent encoding", "QByteArray", |
1615 varlist.append(("percent encoding", "QByteArray", |
1568 "{0}".format(value.toPercentEncoding())[2:-1])) |
1616 "{0}".format(value.toPercentEncoding())[2:-1])) |
1569 elif qttype == 'QPoint': |
1617 elif qttype == 'QPoint': |
1570 varlist.append(("x", "int", "{0:d}".format(value.x()))) |
1618 varlist.append(("x", "int", "{0:d}".format(value.x()))) |
1571 varlist.append(("y", "int", "{0:d}".format(value.y()))) |
1619 varlist.append(("y", "int", "{0:d}".format(value.y()))) |
1589 varlist.append(("width", "float", "{0:g}".format(value.width()))) |
1637 varlist.append(("width", "float", "{0:g}".format(value.width()))) |
1590 varlist.append(("height", "float", "{0:g}".format(value.height()))) |
1638 varlist.append(("height", "float", "{0:g}".format(value.height()))) |
1591 elif qttype == 'QColor': |
1639 elif qttype == 'QColor': |
1592 varlist.append(("name", "str", "{0}".format(value.name()))) |
1640 varlist.append(("name", "str", "{0}".format(value.name()))) |
1593 r, g, b, a = value.getRgb() |
1641 r, g, b, a = value.getRgb() |
1594 varlist.append(("rgb", "int", |
1642 varlist.append( |
1595 "{0:d}, {1:d}, {2:d}, {3:d}".format(r, g, b, a))) |
1643 ("rgb", "int", |
|
1644 "{0:d}, {1:d}, {2:d}, {3:d}".format(r, g, b, a))) |
1596 h, s, v, a = value.getHsv() |
1645 h, s, v, a = value.getHsv() |
1597 varlist.append(("hsv", "int", |
1646 varlist.append( |
1598 "{0:d}, {1:d}, {2:d}, {3:d}".format(h, s, v, a))) |
1647 ("hsv", "int", |
|
1648 "{0:d}, {1:d}, {2:d}, {3:d}".format(h, s, v, a))) |
1599 c, m, y, k, a = value.getCmyk() |
1649 c, m, y, k, a = value.getCmyk() |
1600 varlist.append(("cmyk", "int", |
1650 varlist.append( |
1601 "{0:d}, {1:d}, {2:d}, {3:d}, {4:d}".format(c, m, y, k, a))) |
1651 ("cmyk", "int", |
|
1652 "{0:d}, {1:d}, {2:d}, {3:d}, {4:d}".format(c, m, y, k, a))) |
1602 elif qttype == 'QDate': |
1653 elif qttype == 'QDate': |
1603 varlist.append(("", "QDate", "{0}".format(value.toString()))) |
1654 varlist.append(("", "QDate", "{0}".format(value.toString()))) |
1604 elif qttype == 'QTime': |
1655 elif qttype == 'QTime': |
1605 varlist.append(("", "QTime", "{0}".format(value.toString()))) |
1656 varlist.append(("", "QTime", "{0}".format(value.toString()))) |
1606 elif qttype == 'QDateTime': |
1657 elif qttype == 'QDateTime': |
1613 "{0}".format(value.canonicalPath()))) |
1664 "{0}".format(value.canonicalPath()))) |
1614 elif qttype == 'QFile': |
1665 elif qttype == 'QFile': |
1615 varlist.append(("fileName", "str", "{0}".format(value.fileName()))) |
1666 varlist.append(("fileName", "str", "{0}".format(value.fileName()))) |
1616 elif qttype == 'QFont': |
1667 elif qttype == 'QFont': |
1617 varlist.append(("family", "str", "{0}".format(value.family()))) |
1668 varlist.append(("family", "str", "{0}".format(value.family()))) |
1618 varlist.append(("pointSize", "int", "{0:d}".format(value.pointSize()))) |
1669 varlist.append( |
|
1670 ("pointSize", "int", "{0:d}".format(value.pointSize()))) |
1619 varlist.append(("weight", "int", "{0:d}".format(value.weight()))) |
1671 varlist.append(("weight", "int", "{0:d}".format(value.weight()))) |
1620 varlist.append(("bold", "bool", "{0}".format(value.bold()))) |
1672 varlist.append(("bold", "bool", "{0}".format(value.bold()))) |
1621 varlist.append(("italic", "bool", "{0}".format(value.italic()))) |
1673 varlist.append(("italic", "bool", "{0}".format(value.italic()))) |
1622 elif qttype == 'QUrl': |
1674 elif qttype == 'QUrl': |
1623 varlist.append(("url", "str", "{0}".format(value.toString()))) |
1675 varlist.append(("url", "str", "{0}".format(value.toString()))) |
1630 elif qttype == 'QModelIndex': |
1682 elif qttype == 'QModelIndex': |
1631 varlist.append(("valid", "bool", "{0}".format(value.isValid()))) |
1683 varlist.append(("valid", "bool", "{0}".format(value.isValid()))) |
1632 if value.isValid(): |
1684 if value.isValid(): |
1633 varlist.append(("row", "int", "{0}".format(value.row()))) |
1685 varlist.append(("row", "int", "{0}".format(value.row()))) |
1634 varlist.append(("column", "int", "{0}".format(value.column()))) |
1686 varlist.append(("column", "int", "{0}".format(value.column()))) |
1635 varlist.append(("internalId", "int", "{0}".format(value.internalId()))) |
1687 varlist.append( |
|
1688 ("internalId", "int", "{0}".format(value.internalId()))) |
1636 varlist.append(("internalPointer", "void *", |
1689 varlist.append(("internalPointer", "void *", |
1637 "{0}".format(value.internalPointer()))) |
1690 "{0}".format(value.internalPointer()))) |
1638 elif qttype == 'QRegExp': |
1691 elif qttype == 'QRegExp': |
1639 varlist.append(("pattern", "str", "{0}".format(value.pattern()))) |
1692 varlist.append(("pattern", "str", "{0}".format(value.pattern()))) |
1640 |
1693 |
1641 # GUI stuff |
1694 # GUI stuff |
1642 elif qttype == 'QAction': |
1695 elif qttype == 'QAction': |
1643 varlist.append(("name", "str", "{0}".format(value.objectName()))) |
1696 varlist.append(("name", "str", "{0}".format(value.objectName()))) |
1644 varlist.append(("text", "str", "{0}".format(value.text()))) |
1697 varlist.append(("text", "str", "{0}".format(value.text()))) |
1645 varlist.append(("icon text", "str", "{0}".format(value.iconText()))) |
1698 varlist.append( |
|
1699 ("icon text", "str", "{0}".format(value.iconText()))) |
1646 varlist.append(("tooltip", "str", "{0}".format(value.toolTip()))) |
1700 varlist.append(("tooltip", "str", "{0}".format(value.toolTip()))) |
1647 varlist.append(("whatsthis", "str", "{0}".format(value.whatsThis()))) |
1701 varlist.append( |
1648 varlist.append(("shortcut", "str", |
1702 ("whatsthis", "str", "{0}".format(value.whatsThis()))) |
1649 "{0}".format(value.shortcut().toString()))) |
1703 varlist.append( |
|
1704 ("shortcut", "str", |
|
1705 "{0}".format(value.shortcut().toString()))) |
1650 elif qttype == 'QKeySequence': |
1706 elif qttype == 'QKeySequence': |
1651 varlist.append(("value", "", "{0}".format(value.toString()))) |
1707 varlist.append(("value", "", "{0}".format(value.toString()))) |
1652 |
1708 |
1653 # XML stuff |
1709 # XML stuff |
1654 elif qttype == 'QDomAttr': |
1710 elif qttype == 'QDomAttr': |
1666 elif qttype == 'QDomText': |
1722 elif qttype == 'QDomText': |
1667 varlist.append(("data", "str", "{0}".format(value.data()))) |
1723 varlist.append(("data", "str", "{0}".format(value.data()))) |
1668 |
1724 |
1669 # Networking stuff |
1725 # Networking stuff |
1670 elif qttype == 'QHostAddress': |
1726 elif qttype == 'QHostAddress': |
1671 varlist.append(("address", "QHostAddress", "{0}".format(value.toString()))) |
1727 varlist.append( |
|
1728 ("address", "QHostAddress", "{0}".format(value.toString()))) |
1672 |
1729 |
1673 return varlist |
1730 return varlist |
1674 |
1731 |
1675 def __formatVariablesList(self, keylist, dict, scope, filter=[], |
1732 def __formatVariablesList(self, keylist, dict, scope, filter=[], |
1676 formatSequences=False): |
1733 formatSequences=False): |
1677 """ |
1734 """ |
1678 Private method to produce a formated variables list. |
1735 Private method to produce a formated variables list. |
1679 |
1736 |
1680 The dictionary passed in to it is scanned. Variables are |
1737 The dictionary passed in to it is scanned. Variables are |
1681 only added to the list, if their type is not contained |
1738 only added to the list, if their type is not contained |
1682 in the filter list and their name doesn't match any of the filter expressions. |
1739 in the filter list and their name doesn't match any of the filter |
1683 The formated variables list (a list of tuples of 3 values) is returned. |
1740 expressions. The formated variables list (a list of tuples of 3 |
|
1741 values) is returned. |
1684 |
1742 |
1685 @param keylist keys of the dictionary |
1743 @param keylist keys of the dictionary |
1686 @param dict the dictionary to be scanned |
1744 @param dict the dictionary to be scanned |
1687 @param scope 1 to filter using the globals filter, 0 using the locals |
1745 @param scope 1 to filter using the globals filter, 0 using the locals |
1688 filter (int). |
1746 filter (int). |
1689 Variables are only added to the list, if their name do not match any of the |
1747 Variables are only added to the list, if their name do not match |
1690 filter expressions. |
1748 any of the filter expressions. |
1691 @param filter the indices of variable types to be filtered. Variables are |
1749 @param filter the indices of variable types to be filtered. Variables |
1692 only added to the list, if their type is not contained in the filter |
1750 are only added to the list, if their type is not contained in the |
1693 list. |
1751 filter list. |
1694 @param formatSequences flag indicating, that sequence or dictionary variables |
1752 @param formatSequences flag indicating, that sequence or dictionary |
1695 should be formatted. If it is 0 (or false), just the number of items contained |
1753 variables should be formatted. If it is 0 (or false), just the |
1696 in these variables is returned. (boolean) |
1754 number of items contained in these variables is returned. (boolean) |
1697 @return A tuple consisting of a list of formatted variables. Each variable |
1755 @return A tuple consisting of a list of formatted variables. Each |
1698 entry is a tuple of three elements, the variable name, its type and |
1756 variable entry is a tuple of three elements, the variable name, |
1699 value. |
1757 its type and value. |
1700 """ |
1758 """ |
1701 varlist = [] |
1759 varlist = [] |
1702 if scope: |
1760 if scope: |
1703 patternFilterObjects = self.globalsFilterObjects |
1761 patternFilterObjects = self.globalsFilterObjects |
1704 else: |
1762 else: |
1729 valtype = valtypestr[7:-1] |
1787 valtype = valtypestr[7:-1] |
1730 if valtype not in ConfigVarTypeStrings: |
1788 if valtype not in ConfigVarTypeStrings: |
1731 if ConfigVarTypeStrings.index('instance') in filter: |
1789 if ConfigVarTypeStrings.index('instance') in filter: |
1732 continue |
1790 continue |
1733 elif valtype == "sip.methoddescriptor": |
1791 elif valtype == "sip.methoddescriptor": |
1734 if ConfigVarTypeStrings.index('instance method') in filter: |
1792 if ConfigVarTypeStrings.index( |
|
1793 'instance method') in filter: |
1735 continue |
1794 continue |
1736 elif valtype == "sip.enumtype": |
1795 elif valtype == "sip.enumtype": |
1737 if ConfigVarTypeStrings.index('class') in filter: |
1796 if ConfigVarTypeStrings.index('class') in filter: |
1738 continue |
1797 continue |
1739 valtype = valtypestr |
1798 valtype = valtypestr |
1741 try: |
1800 try: |
1742 if ConfigVarTypeStrings.index(valtype) in filter: |
1801 if ConfigVarTypeStrings.index(valtype) in filter: |
1743 continue |
1802 continue |
1744 except ValueError: |
1803 except ValueError: |
1745 if valtype == "classobj": |
1804 if valtype == "classobj": |
1746 if ConfigVarTypeStrings.index('instance') in filter: |
1805 if ConfigVarTypeStrings.index( |
|
1806 'instance') in filter: |
1747 continue |
1807 continue |
1748 elif valtype == "sip.methoddescriptor": |
1808 elif valtype == "sip.methoddescriptor": |
1749 if ConfigVarTypeStrings.index('instance method') in filter: |
1809 if ConfigVarTypeStrings.index( |
|
1810 'instance method') in filter: |
1750 continue |
1811 continue |
1751 elif valtype == "sip.enumtype": |
1812 elif valtype == "sip.enumtype": |
1752 if ConfigVarTypeStrings.index('class') in filter: |
1813 if ConfigVarTypeStrings.index('class') in filter: |
1753 continue |
1814 continue |
1754 elif not valtype.startswith("PySide") and \ |
1815 elif not valtype.startswith("PySide") and \ |
1829 |
1890 |
1830 self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion, |
1891 self.write("{0}{1}||{2}\n".format(DebugProtocol.ResponseCompletion, |
1831 str(completions), text)) |
1892 str(completions), text)) |
1832 |
1893 |
1833 def startDebugger(self, filename=None, host=None, port=None, |
1894 def startDebugger(self, filename=None, host=None, port=None, |
1834 enableTrace=True, exceptions=True, tracePython=False, redirect=True): |
1895 enableTrace=True, exceptions=True, tracePython=False, |
|
1896 redirect=True): |
1835 """ |
1897 """ |
1836 Public method used to start the remote debugger. |
1898 Public method used to start the remote debugger. |
1837 |
1899 |
1838 @param filename the program to be debugged (string) |
1900 @param filename the program to be debugged (string) |
1839 @param host hostname of the debug server (string) |
1901 @param host hostname of the debug server (string) |
1840 @param port portnumber of the debug server (int) |
1902 @param port portnumber of the debug server (int) |
1841 @param enableTrace flag to enable the tracing function (boolean) |
1903 @param enableTrace flag to enable the tracing function (boolean) |
1842 @param exceptions flag to enable exception reporting of the IDE (boolean) |
1904 @param exceptions flag to enable exception reporting of the IDE |
1843 @param tracePython flag to enable tracing into the Python library (boolean) |
1905 (boolean) |
1844 @param redirect flag indicating redirection of stdin, stdout and stderr (boolean) |
1906 @param tracePython flag to enable tracing into the Python library |
|
1907 (boolean) |
|
1908 @param redirect flag indicating redirection of stdin, stdout and |
|
1909 stderr (boolean) |
1845 """ |
1910 """ |
1846 global debugClient |
1911 global debugClient |
1847 if host is None: |
1912 if host is None: |
1848 host = os.getenv('ERICHOST', 'localhost') |
1913 host = os.getenv('ERICHOST', 'localhost') |
1849 if port is None: |
1914 if port is None: |
1891 @param progargs commandline for the program to be debugged |
1956 @param progargs commandline for the program to be debugged |
1892 (list of strings) |
1957 (list of strings) |
1893 @param wd working directory for the program execution (string) |
1958 @param wd working directory for the program execution (string) |
1894 @param host hostname of the debug server (string) |
1959 @param host hostname of the debug server (string) |
1895 @param port portnumber of the debug server (int) |
1960 @param port portnumber of the debug server (int) |
1896 @param exceptions flag to enable exception reporting of the IDE (boolean) |
1961 @param exceptions flag to enable exception reporting of the IDE |
1897 @param tracePython flag to enable tracing into the Python library (boolean) |
1962 (boolean) |
1898 @param redirect flag indicating redirection of stdin, stdout and stderr (boolean) |
1963 @param tracePython flag to enable tracing into the Python library |
|
1964 (boolean) |
|
1965 @param redirect flag indicating redirection of stdin, stdout and |
|
1966 stderr (boolean) |
1899 """ |
1967 """ |
1900 if host is None: |
1968 if host is None: |
1901 host = os.getenv('ERICHOST', 'localhost') |
1969 host = os.getenv('ERICHOST', 'localhost') |
1902 if port is None: |
1970 if port is None: |
1903 port = os.getenv('ERICPORT', 42424) |
1971 port = os.getenv('ERICPORT', 42424) |
1933 sys.excepthook = self.__unhandled_exception |
2001 sys.excepthook = self.__unhandled_exception |
1934 |
2002 |
1935 # This will eventually enter a local event loop. |
2003 # This will eventually enter a local event loop. |
1936 # Note the use of backquotes to cause a repr of self.running. The |
2004 # Note the use of backquotes to cause a repr of self.running. The |
1937 # need for this is on Windows os where backslash is the path separator. |
2005 # need for this is on Windows os where backslash is the path separator. |
1938 # They will get inadvertantly stripped away during the eval causing IOErrors |
2006 # They will get inadvertantly stripped away during the eval causing |
1939 # if self.running is passed as a normal str. |
2007 # IOErrors if self.running is passed as a normal str. |
1940 self.debugMod.__dict__['__file__'] = self.running |
2008 self.debugMod.__dict__['__file__'] = self.running |
1941 sys.modules['__main__'] = self.debugMod |
2009 sys.modules['__main__'] = self.debugMod |
1942 res = self.mainThread.run('exec(open(' + repr(self.running) + ').read())', |
2010 res = self.mainThread.run( |
1943 self.debugMod.__dict__) |
2011 'exec(open(' + repr(self.running) + ').read())', |
|
2012 self.debugMod.__dict__) |
1944 self.progTerminated(res) |
2013 self.progTerminated(res) |
1945 |
2014 |
1946 def run_call(self, scriptname, func, *args): |
2015 def run_call(self, scriptname, func, *args): |
1947 """ |
2016 """ |
1948 Public method used to start the remote debugger and call a function. |
2017 Public method used to start the remote debugger and call a function. |
2113 environment variable. |
2184 environment variable. |
2114 |
2185 |
2115 @param firstEntry entry to be put first in sys.path (string) |
2186 @param firstEntry entry to be put first in sys.path (string) |
2116 @return path list for use as sys.path (list of strings) |
2187 @return path list for use as sys.path (list of strings) |
2117 """ |
2188 """ |
2118 sysPath = [path for path in os.environ.get("PYTHONPATH", "").split(os.pathsep) |
2189 sysPath = [path for path in os.environ.get("PYTHONPATH", "") |
|
2190 .split(os.pathsep) |
2119 if path not in sys.path] + sys.path[:] |
2191 if path not in sys.path] + sys.path[:] |
2120 if "" in sysPath: |
2192 if "" in sysPath: |
2121 sysPath.remove("") |
2193 sysPath.remove("") |
2122 sysPath.insert(0, firstEntry) |
2194 sysPath.insert(0, firstEntry) |
2123 sysPath.insert(0, '') |
2195 sysPath.insert(0, '') |