eric6/DebugClients/Python/DebugClientBase.py

branch
multi_processing
changeset 7871
eb65864ca038
parent 7870
ab8f95bc7d2d
child 7872
433dacbfa456
equal deleted inserted replaced
7870:ab8f95bc7d2d 7871:eb65864ca038
29 from AsyncFile import AsyncFile, AsyncPendingWrite 29 from AsyncFile import AsyncFile, AsyncPendingWrite
30 from DebugConfig import ConfigQtNames, SpecialAttributes 30 from DebugConfig import ConfigQtNames, SpecialAttributes
31 from FlexCompleter import Completer 31 from FlexCompleter import Completer
32 from DebugUtilities import prepareJsonCommand 32 from DebugUtilities import prepareJsonCommand
33 from BreakpointWatch import Breakpoint, Watch 33 from BreakpointWatch import Breakpoint, Watch
34 ##from MultiProcessDebugExtension import patchNewProcessFunctions 34 from MultiProcessDebugExtension import patchNewProcessFunctions
35 35
36 from DebugUtilities import getargvalues, formatargvalues 36 from DebugUtilities import getargvalues, formatargvalues
37 37
38 DebugClientInstance = None 38 DebugClientInstance = None
39 39
66 __main__.__builtins__.__dict__['input'] = DebugClientInput 66 __main__.__builtins__.__dict__['input'] = DebugClientInput
67 67
68 ############################################################################### 68 ###############################################################################
69 69
70 70
71 def DebugClientFork():
72 """
73 Replacement for the standard os.fork().
74
75 @return result of the fork() call
76 """
77 if DebugClientInstance is None:
78 return DebugClientOrigFork()
79
80 return DebugClientInstance.fork()
81
82 # use our own fork().
83 if 'fork' in dir(os):
84 DebugClientOrigFork = os.fork
85 os.fork = DebugClientFork
86
87 ###############################################################################
88
89
90 def DebugClientClose(fd): 71 def DebugClientClose(fd):
91 """ 72 """
92 Replacement for the standard os.close(fd). 73 Replacement for the standard os.close(fd).
93 74
94 @param fd open file descriptor to be closed (integer) 75 @param fd open file descriptor to be closed (integer)
180 self.running = None 161 self.running = None
181 self.test = None 162 self.test = None
182 self.debugging = False 163 self.debugging = False
183 self.multiprocessSupport = False 164 self.multiprocessSupport = False
184 self.noDebugList = [] 165 self.noDebugList = []
185
186 self.fork_auto = False
187 self.fork_child = False
188 166
189 self.readstream = None 167 self.readstream = None
190 self.writestream = None 168 self.writestream = None
191 self.errorstream = None 169 self.errorstream = None
192 self.pollingDisabled = False 170 self.pollingDisabled = False
282 260
283 def __compileFileSource(self, filename, mode='exec'): 261 def __compileFileSource(self, filename, mode='exec'):
284 """ 262 """
285 Private method to compile source code read from a file. 263 Private method to compile source code read from a file.
286 264
287 @param filename name of the source file (string) 265 @param filename name of the source file
288 @param mode kind of code to be generated (string, exec or eval) 266 @type str
267 @param mode kind of code to be generated (exec or eval)
268 @type str
289 @return compiled code object (None in case of errors) 269 @return compiled code object (None in case of errors)
290 """ 270 """
291 with codecs.open(filename, encoding=self.__coding) as fp: 271 with codecs.open(filename, encoding=self.__coding) as fp:
292 statement = fp.read() 272 statement = fp.read()
293 273
274 return self.__compileCommand(statement, filename=filename, mode=mode)
275
276 def __compileCommand(self, statement, filename="<string>", mode="exec"):
277 """
278 Private method to compile source code.
279
280 @param statement source code string to be compiled
281 @type str
282 @param filename name of the source file
283 @type str
284 @param mode kind of code to be generated (exec or eval)
285 @type str
286 @return compiled code object (None in case of errors)
287 """
294 try: 288 try:
295 code = compile(statement + '\n', filename, mode) 289 code = compile(statement + '\n', filename, mode)
296 except SyntaxError: 290 except SyntaxError:
297 exctype, excval, exctb = sys.exc_info() 291 exctype, excval, exctb = sys.exc_info()
298 try: 292 try:
430 424
431 self.running = sys.argv[0] 425 self.running = sys.argv[0]
432 self.debugging = True 426 self.debugging = True
433 self.multiprocessSupport = params["multiprocess"] 427 self.multiprocessSupport = params["multiprocess"]
434 428
435 self.fork_auto = params["autofork"]
436 self.fork_child = params["forkChild"]
437
438 self.threads.clear() 429 self.threads.clear()
439 self.attachThread(mainThread=True) 430 self.attachThread(mainThread=True)
440 431
441 # set the system exception handling function to ensure, that 432 # set the system exception handling function to ensure, that
442 # we report on all unhandled exceptions 433 # we report on all unhandled exceptions
470 else: 461 else:
471 os.chdir(params["workdir"]) 462 os.chdir(params["workdir"])
472 463
473 self.running = sys.argv[0] 464 self.running = sys.argv[0]
474 self.botframe = None 465 self.botframe = None
475
476 self.fork_auto = params["autofork"]
477 self.fork_child = params["forkChild"]
478 466
479 self.threads.clear() 467 self.threads.clear()
480 self.attachThread(mainThread=True) 468 self.attachThread(mainThread=True)
481 469
482 # set the system exception handling function to ensure, that 470 # set the system exception handling function to ensure, that
930 "status": 0 if result.wasSuccessful() else 1, 918 "status": 0 if result.wasSuccessful() else 1,
931 }) 919 })
932 920
933 elif method == "RequestUTStop": 921 elif method == "RequestUTStop":
934 self.testResult.stop() 922 self.testResult.stop()
935
936 elif method == "ResponseForkTo":
937 # this results from a separate event loop
938 self.fork_child = (params["target"] == 'child')
939 self.eventExit = True
940 923
941 def __assembleTestCasesList(self, suite, start): 924 def __assembleTestCasesList(self, suite, start):
942 """ 925 """
943 Private method to assemble a list of test cases included in a test 926 Private method to assemble a list of test cases included in a test
944 suite. 927 suite.
2052 self.mainThread.set_trace() 2035 self.mainThread.set_trace()
2053 2036
2054 def startProgInDebugger(self, progargs, wd='', host=None, 2037 def startProgInDebugger(self, progargs, wd='', host=None,
2055 port=None, exceptions=True, tracePython=False, 2038 port=None, exceptions=True, tracePython=False,
2056 redirect=True, passive=True, 2039 redirect=True, passive=True,
2057 multiprocessSupport=False): 2040 multiprocessSupport=False, codeStr=""):
2058 """ 2041 """
2059 Public method used to start the remote debugger. 2042 Public method used to start the remote debugger.
2060 2043
2061 @param progargs commandline for the program to be debugged 2044 @param progargs commandline for the program to be debugged
2062 (list of strings) 2045 (list of strings)
2072 @param passive flag indicating a passive debugging session 2055 @param passive flag indicating a passive debugging session
2073 @type bool 2056 @type bool
2074 @param multiprocessSupport flag indicating to enable multiprocess 2057 @param multiprocessSupport flag indicating to enable multiprocess
2075 debugging support 2058 debugging support
2076 @type bool 2059 @type bool
2060 @param codeStr string containing Python code to execute
2061 @type str
2077 @return exit code of the debugged program 2062 @return exit code of the debugged program
2078 @rtype int 2063 @rtype int
2079 """ 2064 """
2080 if host is None: 2065 if host is None:
2081 host = os.getenv('ERICHOST', 'localhost') 2066 host = os.getenv('ERICHOST', 'localhost')
2086 name = os.path.basename(progargs[0]) 2071 name = os.path.basename(progargs[0])
2087 self.connectDebugger(port, remoteAddress, redirect, name=name) 2072 self.connectDebugger(port, remoteAddress, redirect, name=name)
2088 2073
2089 self._fncache = {} 2074 self._fncache = {}
2090 self.dircache = [] 2075 self.dircache = []
2091 sys.argv = progargs[:] 2076 if codeStr:
2092 sys.argv[0] = os.path.abspath(sys.argv[0]) 2077 self.running = "<string>"
2093 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0])) 2078 sys.argv = ["<string>"] + progargs[:]
2094 if wd == '':
2095 os.chdir(sys.path[1])
2096 else: 2079 else:
2097 os.chdir(wd) 2080 sys.argv = progargs[:]
2098 self.running = sys.argv[0] 2081 sys.argv[0] = os.path.abspath(sys.argv[0])
2099 self.__setCoding(self.running) 2082 sys.path = self.__getSysPath(os.path.dirname(sys.argv[0]))
2083 if wd == '':
2084 os.chdir(sys.path[1])
2085 else:
2086 os.chdir(wd)
2087 self.running = sys.argv[0]
2088 self.__setCoding(self.running)
2100 self.debugging = True 2089 self.debugging = True
2101 self.multiprocessSupport = multiprocessSupport 2090 self.multiprocessSupport = multiprocessSupport
2102 2091
2103 self.passive = passive 2092 self.passive = passive
2104 if passive: 2093 if passive:
2114 self.__interceptSignals() 2103 self.__interceptSignals()
2115 2104
2116 # This will eventually enter a local event loop. 2105 # This will eventually enter a local event loop.
2117 self.debugMod.__dict__['__file__'] = self.running 2106 self.debugMod.__dict__['__file__'] = self.running
2118 sys.modules['__main__'] = self.debugMod 2107 sys.modules['__main__'] = self.debugMod
2119 code = self.__compileFileSource(self.running) 2108 if codeStr:
2109 code = self.__compileCommand(codeStr)
2110 else:
2111 code = self.__compileFileSource(self.running)
2120 if code: 2112 if code:
2121 res = self.mainThread.run(code, self.debugMod.__dict__, debug=True) 2113 res = self.mainThread.run(code, self.debugMod.__dict__, debug=True)
2122 else: 2114 else:
2123 res = 42 # should not happen 2115 res = 42 # should not happen
2124 return res 2116 return res
2177 tracePython = False 2169 tracePython = False
2178 exceptions = True 2170 exceptions = True
2179 redirect = True 2171 redirect = True
2180 passive = True 2172 passive = True
2181 multiprocess = False 2173 multiprocess = False
2174 hasCode = False
2182 while args[0]: 2175 while args[0]:
2183 if args[0] == '-h': 2176 if args[0] == '-h':
2184 host = args[1] 2177 host = args[1]
2185 del args[0] 2178 del args[0]
2186 del args[0] 2179 del args[0]
2202 redirect = False 2195 redirect = False
2203 del args[0] 2196 del args[0]
2204 elif args[0] == '--no-encoding': 2197 elif args[0] == '--no-encoding':
2205 self.noencoding = True 2198 self.noencoding = True
2206 del args[0] 2199 del args[0]
2207 elif args[0] == '--fork-child':
2208 self.fork_auto = True
2209 self.fork_child = True
2210 del args[0]
2211 elif args[0] == '--fork-parent':
2212 self.fork_auto = True
2213 self.fork_child = False
2214 del args[0]
2215 elif args[0] == '--no-passive': 2200 elif args[0] == '--no-passive':
2216 passive = False 2201 passive = False
2217 del args[0] 2202 del args[0]
2218 elif args[0] == '--multiprocess': 2203 elif args[0] == '--multiprocess':
2219 multiprocess = True 2204 multiprocess = True
2205 del args[0]
2206 elif args[0] == '--code':
2207 hasCode = True
2220 del args[0] 2208 del args[0]
2221 elif args[0] == '--': 2209 elif args[0] == '--':
2222 del args[0] 2210 del args[0]
2223 break 2211 break
2224 else: # unknown option 2212 else: # unknown option
2232 else: 2220 else:
2233 # Store options if a process is spawn 2221 # Store options if a process is spawn
2234 # TODO: check which ones are really needed 2222 # TODO: check which ones are really needed
2235 self.startOptions = ( 2223 self.startOptions = (
2236 wd, host, port, exceptions, tracePython, redirect, 2224 wd, host, port, exceptions, tracePython, redirect,
2237 self.noencoding, self.fork_auto, self.fork_child, 2225 self.noencoding,
2238 ) 2226 )
2239 if not self.noencoding: 2227 if not self.noencoding:
2240 self.__coding = self.defaultCoding 2228 self.__coding = self.defaultCoding
2241 ## patchNewProcessFunctions(multiprocess, self) 2229 patchNewProcessFunctions(multiprocess, self)
2230 if hasCode:
2231 codeStr = args.pop(0)
2232 else:
2233 codeStr=""
2242 res = self.startProgInDebugger( 2234 res = self.startProgInDebugger(
2243 args, wd, host, port, exceptions=exceptions, 2235 args, wd, host, port, exceptions=exceptions,
2244 tracePython=tracePython, redirect=redirect, 2236 tracePython=tracePython, redirect=redirect,
2245 passive=passive, multiprocessSupport=multiprocess 2237 passive=passive, multiprocessSupport=multiprocess,
2238 codeStr=codeStr
2246 ) 2239 )
2247 sys.exit(res) 2240 sys.exit(res)
2248 else: 2241 else:
2249 if sys.argv[1] == '--no-encoding': 2242 if sys.argv[1] == '--no-encoding':
2250 self.noencoding = True 2243 self.noencoding = True
2289 if port >= 0: 2282 if port >= 0:
2290 # Store options if a process is spawn 2283 # Store options if a process is spawn
2291 # TODO: check which ones are really needed 2284 # TODO: check which ones are really needed
2292 self.startOptions = ( 2285 self.startOptions = (
2293 '', remoteAddress, port, True, False, redirect, 2286 '', remoteAddress, port, True, False, redirect,
2294 self.noencoding, self.fork_auto, self.fork_child, 2287 self.noencoding,
2295 ) 2288 )
2296 if not self.noencoding: 2289 if not self.noencoding:
2297 self.__coding = self.defaultCoding 2290 self.__coding = self.defaultCoding
2291 patchNewProcessFunctions(self.multiprocessSupport, self)
2298 self.connectDebugger(port, remoteAddress, redirect) 2292 self.connectDebugger(port, remoteAddress, redirect)
2299 ## patchNewProcessFunctions(self.multiprocessSupport, self)
2300 self.__interact() 2293 self.__interact()
2301 else: 2294 else:
2302 print("No network port given. Aborting...") 2295 print("No network port given. Aborting...")
2303 # __IGNORE_WARNING_M801__ 2296 # __IGNORE_WARNING_M801__
2304
2305 def fork(self):
2306 """
2307 Public method implementing a fork routine deciding which branch
2308 to follow.
2309
2310 @return process ID (integer)
2311 """
2312 ## if not self.fork_auto:
2313 ## self.sendJsonCommand("RequestForkTo", {})
2314 ## self.eventLoop(True)
2315 pid = DebugClientOrigFork()
2316 if pid == 0:
2317 # child
2318 ## if not self.fork_child:
2319 sys.settrace(None)
2320 sys.setprofile(None)
2321 self.sessionClose(False)
2322 ## (wd, host, port, exceptions, tracePython, redirect,
2323 ## noencoding, fork_auto, fork_child) = self.startOptions
2324 ## self.startDebugger(sys.argv[0], host, port,
2325 ## exceptions=exceptions,
2326 ## tracePython=tracePython,
2327 ## redirect=redirect,
2328 ## passive=False)
2329 else:
2330 # parent
2331 if self.fork_child:
2332 sys.settrace(None)
2333 sys.setprofile(None)
2334 self.sessionClose(False)
2335 return pid
2336 2297
2337 def close(self, fd): 2298 def close(self, fd):
2338 """ 2299 """
2339 Public method implementing a close method as a replacement for 2300 Public method implementing a close method as a replacement for
2340 os.close(). 2301 os.close().

eric ide

mercurial