diff options
author | Kurt B. Kaiser <kbk@shore.net> | 2003-02-17 18:57:16 (GMT) |
---|---|---|
committer | Kurt B. Kaiser <kbk@shore.net> | 2003-02-17 18:57:16 (GMT) |
commit | 003091cd51c5278e3ef76b6db01bd719b8b1c416 (patch) | |
tree | afc0ea2b00d259d40f2eebd7beb33bd96a4b9eba /Lib/idlelib/rpc.py | |
parent | f927f14edab27b128b1962314c1cf7c10be73ac5 (diff) | |
download | cpython-003091cd51c5278e3ef76b6db01bd719b8b1c416.zip cpython-003091cd51c5278e3ef76b6db01bd719b8b1c416.tar.gz cpython-003091cd51c5278e3ef76b6db01bd719b8b1c416.tar.bz2 |
M NEWS.txt
M PyShell.py
M ScriptBinding.py
M rpc.py
M run.py
Clean up the way IDLEfork handles termination of the subprocess, restore
ability to interrupt user code in Windows (so long as it's doing terminal
I/O).
1. Handle subprocess interrupts in Windows with an RPC message.
2. Run/F5 will restart the subprocess even if user code is running.
3. Restart the subprocess if the link is dropped.
4. Exit IDLE cleanly even during I/O.
4. In rpc.py, remove explicit calls to statelock, let the condition
variable handle acquire() and release().
Diffstat (limited to 'Lib/idlelib/rpc.py')
-rw-r--r-- | Lib/idlelib/rpc.py | 60 |
1 files changed, 43 insertions, 17 deletions
diff --git a/Lib/idlelib/rpc.py b/Lib/idlelib/rpc.py index b50643a..c79f4fe 100644 --- a/Lib/idlelib/rpc.py +++ b/Lib/idlelib/rpc.py @@ -86,6 +86,16 @@ class RPCServer(SocketServer.TCPServer): "Override TCPServer method, return already connected socket" return self.socket, self.server_address + def handle_error(self, request, client_address): + """Override TCPServer method, no error message if exiting""" + try: + raise + except SystemExit: + raise + else: + TCPServer.handle_error(request, client_address) + + objecttable = {} class SocketIO: @@ -100,9 +110,10 @@ class SocketIO: if objtable is None: objtable = objecttable self.objtable = objtable - self.statelock = threading.Lock() + self.cvar = threading.Condition() self.responses = {} self.cvars = {} + self.interrupted = False def close(self): sock = self.sock @@ -153,13 +164,16 @@ class SocketIO: if isinstance(ret, RemoteObject): ret = remoteref(ret) return ("OK", ret) + except SystemExit: + raise except: self.debug("localcall:EXCEPTION") + if self.debugging: traceback.print_exc(file=sys.__stderr__) efile = sys.stderr typ, val, tb = info = sys.exc_info() sys.last_type, sys.last_value, sys.last_traceback = info tbe = traceback.extract_tb(tb) - print >>efile, 'Traceback (most recent call last):' + print >>efile, '\nTraceback (most recent call last):' exclude = ("run.py", "rpc.py", "RemoteDebugger.py", "bdb.py") self.cleanup_traceback(tbe, exclude) traceback.print_list(tbe, file=efile) @@ -186,9 +200,9 @@ class SocketIO: break del tb[-1] if len(tb) == 0: - # error was in RPC internals, don't prune! + # exception was in RPC internals, don't prune! tb[:] = orig_tb[:] - print>>sys.stderr, "** RPC Internal Error: ", tb + print>>sys.stderr, "** IDLE RPC Internal Exception: " for i in range(len(tb)): fn, ln, nm, line = tb[i] if nm == '?': @@ -199,7 +213,12 @@ class SocketIO: tb[i] = fn, ln, nm, line def remotecall(self, oid, methodname, args, kwargs): - self.debug("calling asynccall via remotecall") + self.debug("remotecall:asynccall: ", oid, methodname) + # XXX KBK 06Feb03 self.interrupted logic may not be necessary if + # subprocess is threaded. + if self.interrupted: + self.interrupted = False + raise KeyboardInterrupt seq = self.asynccall(oid, methodname, args, kwargs) return self.asyncreturn(seq) @@ -221,7 +240,8 @@ class SocketIO: if how == "OK": return what if how == "EXCEPTION": - raise Exception, "RPC SocketIO.decoderesponse exception" + self.debug("decoderesponse: EXCEPTION") + return None if how == "ERROR": self.debug("decoderesponse: Internal ERROR:", what) raise RuntimeError, what @@ -266,16 +286,15 @@ class SocketIO: return response else: # Auxiliary thread: wait for notification from main thread - cvar = threading.Condition(self.statelock) - self.statelock.acquire() - self.cvars[myseq] = cvar + self.cvar.acquire() + self.cvars[myseq] = self.cvar while not self.responses.has_key(myseq): - cvar.wait() + self.cvar.wait() response = self.responses[myseq] del self.responses[myseq] del self.cvars[myseq] - self.statelock.release() - return response # might be None + self.cvar.release() + return response def newseq(self): self.nextseq = seq = self.nextseq + 2 @@ -290,8 +309,13 @@ class SocketIO: raise s = struct.pack("<i", len(s)) + s while len(s) > 0: - n = self.sock.send(s) - s = s[n:] + try: + n = self.sock.send(s) + except AttributeError: + # socket was closed + raise IOError + else: + s = s[n:] def ioready(self, wait=0.0): r, w, x = select.select([self.sock.fileno()], [], [], wait) @@ -374,12 +398,14 @@ class SocketIO: elif seq == myseq: return resq else: - self.statelock.acquire() - self.responses[seq] = resq + self.cvar.acquire() cv = self.cvars.get(seq) + # response involving unknown sequence number is discarded, + # probably intended for prior incarnation if cv is not None: + self.responses[seq] = resq cv.notify() - self.statelock.release() + self.cvar.release() continue #----------------- end class SocketIO -------------------- |