From 8b7e322fa916a1aa95a803af882fe610295cc6e0 Mon Sep 17 00:00:00 2001 From: Alexander Koshevoy Date: Fri, 26 Oct 2018 11:17:21 +0300 Subject: [PATCH] PY-32247 Response with KeyboardInterruptException in Python Console's requestInput() when hitting Ctrl+C in the IDE --- .../protocol/KeyboardInterruptException.java | 260 ++++++++++++++++++ .../PythonConsoleFrontendService.java | 139 +++++++++- .../pydev/_pydev_bundle/pydev_stdin.py | 6 + .../pydev/pydev_console/console.thrift | 5 +- .../helpers/pydev/pydev_console/protocol.py | 1 + .../console/PydevConsoleCommunication.java | 17 +- 6 files changed, 414 insertions(+), 14 deletions(-) create mode 100644 python/gen/com/jetbrains/python/console/protocol/KeyboardInterruptException.java diff --git a/python/gen/com/jetbrains/python/console/protocol/KeyboardInterruptException.java b/python/gen/com/jetbrains/python/console/protocol/KeyboardInterruptException.java new file mode 100644 index 000000000000..06eaa1de608b --- /dev/null +++ b/python/gen/com/jetbrains/python/console/protocol/KeyboardInterruptException.java @@ -0,0 +1,260 @@ +/** + * Autogenerated by Thrift Compiler (0.11.0) + * + * DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING + * @generated + */ +package com.jetbrains.python.console.protocol; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"}) +@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2018-10-25") +public class KeyboardInterruptException extends org.apache.thrift.TException implements org.apache.thrift.TBase, java.io.Serializable, Cloneable, Comparable { + private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("KeyboardInterruptException"); + + + private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new KeyboardInterruptExceptionStandardSchemeFactory(); + private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new KeyboardInterruptExceptionTupleSchemeFactory(); + + + /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ + public enum _Fields implements org.apache.thrift.TFieldIdEnum { +; + + private static final java.util.Map byName = new java.util.HashMap(); + + static { + for (_Fields field : java.util.EnumSet.allOf(_Fields.class)) { + byName.put(field.getFieldName(), field); + } + } + + /** + * Find the _Fields constant that matches fieldId, or null if its not found. + */ + public static _Fields findByThriftId(int fieldId) { + switch(fieldId) { + default: + return null; + } + } + + /** + * Find the _Fields constant that matches fieldId, throwing an exception + * if it is not found. + */ + public static _Fields findByThriftIdOrThrow(int fieldId) { + _Fields fields = findByThriftId(fieldId); + if (fields == null) throw new java.lang.IllegalArgumentException("Field " + fieldId + " doesn't exist!"); + return fields; + } + + /** + * Find the _Fields constant that matches name, or null if its not found. + */ + public static _Fields findByName(java.lang.String name) { + return byName.get(name); + } + + private final short _thriftId; + private final java.lang.String _fieldName; + + _Fields(short thriftId, java.lang.String fieldName) { + _thriftId = thriftId; + _fieldName = fieldName; + } + + public short getThriftFieldId() { + return _thriftId; + } + + public java.lang.String getFieldName() { + return _fieldName; + } + } + public static final java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> metaDataMap; + static { + java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); + metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); + org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(KeyboardInterruptException.class, metaDataMap); + } + + public KeyboardInterruptException() { + } + + /** + * Performs a deep copy on other. + */ + public KeyboardInterruptException(KeyboardInterruptException other) { + } + + public KeyboardInterruptException deepCopy() { + return new KeyboardInterruptException(this); + } + + @Override + public void clear() { + } + + public void setFieldValue(_Fields field, java.lang.Object value) { + switch (field) { + } + } + + public java.lang.Object getFieldValue(_Fields field) { + switch (field) { + } + throw new java.lang.IllegalStateException(); + } + + /** Returns true if field corresponding to fieldID is set (has been assigned a value) and false otherwise */ + public boolean isSet(_Fields field) { + if (field == null) { + throw new java.lang.IllegalArgumentException(); + } + + switch (field) { + } + throw new java.lang.IllegalStateException(); + } + + @Override + public boolean equals(java.lang.Object that) { + if (that == null) + return false; + if (that instanceof KeyboardInterruptException) + return this.equals((KeyboardInterruptException)that); + return false; + } + + public boolean equals(KeyboardInterruptException that) { + if (that == null) + return false; + if (this == that) + return true; + + return true; + } + + @Override + public int hashCode() { + int hashCode = 1; + + return hashCode; + } + + @Override + public int compareTo(KeyboardInterruptException other) { + if (!getClass().equals(other.getClass())) { + return getClass().getName().compareTo(other.getClass().getName()); + } + + int lastComparison = 0; + + return 0; + } + + public _Fields fieldForId(int fieldId) { + return _Fields.findByThriftId(fieldId); + } + + public void read(org.apache.thrift.protocol.TProtocol iprot) throws org.apache.thrift.TException { + scheme(iprot).read(iprot, this); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot) throws org.apache.thrift.TException { + scheme(oprot).write(oprot, this); + } + + @Override + public java.lang.String toString() { + java.lang.StringBuilder sb = new java.lang.StringBuilder("KeyboardInterruptException("); + boolean first = true; + + sb.append(")"); + return sb.toString(); + } + + public void validate() throws org.apache.thrift.TException { + // check for required fields + // check for sub-struct validity + } + + private void writeObject(java.io.ObjectOutputStream out) throws java.io.IOException { + try { + write(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(out))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private void readObject(java.io.ObjectInputStream in) throws java.io.IOException, java.lang.ClassNotFoundException { + try { + read(new org.apache.thrift.protocol.TCompactProtocol(new org.apache.thrift.transport.TIOStreamTransport(in))); + } catch (org.apache.thrift.TException te) { + throw new java.io.IOException(te); + } + } + + private static class KeyboardInterruptExceptionStandardSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public KeyboardInterruptExceptionStandardScheme getScheme() { + return new KeyboardInterruptExceptionStandardScheme(); + } + } + + private static class KeyboardInterruptExceptionStandardScheme extends org.apache.thrift.scheme.StandardScheme { + + public void read(org.apache.thrift.protocol.TProtocol iprot, KeyboardInterruptException struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TField schemeField; + iprot.readStructBegin(); + while (true) + { + schemeField = iprot.readFieldBegin(); + if (schemeField.type == org.apache.thrift.protocol.TType.STOP) { + break; + } + switch (schemeField.id) { + default: + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + iprot.readFieldEnd(); + } + iprot.readStructEnd(); + + // check for required fields of primitive type, which can't be checked in the validate method + struct.validate(); + } + + public void write(org.apache.thrift.protocol.TProtocol oprot, KeyboardInterruptException struct) throws org.apache.thrift.TException { + struct.validate(); + + oprot.writeStructBegin(STRUCT_DESC); + oprot.writeFieldStop(); + oprot.writeStructEnd(); + } + + } + + private static class KeyboardInterruptExceptionTupleSchemeFactory implements org.apache.thrift.scheme.SchemeFactory { + public KeyboardInterruptExceptionTupleScheme getScheme() { + return new KeyboardInterruptExceptionTupleScheme(); + } + } + + private static class KeyboardInterruptExceptionTupleScheme extends org.apache.thrift.scheme.TupleScheme { + + @Override + public void write(org.apache.thrift.protocol.TProtocol prot, KeyboardInterruptException struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol oprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + } + + @Override + public void read(org.apache.thrift.protocol.TProtocol prot, KeyboardInterruptException struct) throws org.apache.thrift.TException { + org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; + } + } + + private static S scheme(org.apache.thrift.protocol.TProtocol proto) { + return (org.apache.thrift.scheme.StandardScheme.class.equals(proto.getScheme()) ? STANDARD_SCHEME_FACTORY : TUPLE_SCHEME_FACTORY).getScheme(); + } +} + diff --git a/python/gen/com/jetbrains/python/console/protocol/PythonConsoleFrontendService.java b/python/gen/com/jetbrains/python/console/protocol/PythonConsoleFrontendService.java index 5c9dcba05c3f..b11852c12a5e 100644 --- a/python/gen/com/jetbrains/python/console/protocol/PythonConsoleFrontendService.java +++ b/python/gen/com/jetbrains/python/console/protocol/PythonConsoleFrontendService.java @@ -7,14 +7,14 @@ package com.jetbrains.python.console.protocol; @SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused"}) -@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2018-08-07") +@javax.annotation.Generated(value = "Autogenerated by Thrift Compiler (0.11.0)", date = "2018-10-25") public class PythonConsoleFrontendService { public interface Iface { public void notifyFinished(boolean needsMoreInput) throws org.apache.thrift.TException; - public java.lang.String requestInput(java.lang.String path) throws org.apache.thrift.TException; + public java.lang.String requestInput(java.lang.String path) throws KeyboardInterruptException, org.apache.thrift.TException; public void notifyAboutMagic(java.util.List commands, boolean isAutoMagic) throws org.apache.thrift.TException; @@ -88,7 +88,7 @@ public class PythonConsoleFrontendService { return; } - public java.lang.String requestInput(java.lang.String path) throws org.apache.thrift.TException + public java.lang.String requestInput(java.lang.String path) throws KeyboardInterruptException, org.apache.thrift.TException { send_requestInput(path); return recv_requestInput(); @@ -101,13 +101,16 @@ public class PythonConsoleFrontendService { sendBase("requestInput", args); } - public java.lang.String recv_requestInput() throws org.apache.thrift.TException + public java.lang.String recv_requestInput() throws KeyboardInterruptException, org.apache.thrift.TException { requestInput_result result = new requestInput_result(); receiveBase(result, "requestInput"); if (result.isSetSuccess()) { return result.success; } + if (result.interrupted != null) { + throw result.interrupted; + } throw new org.apache.thrift.TApplicationException(org.apache.thrift.TApplicationException.MISSING_RESULT, "requestInput failed: unknown result"); } @@ -268,7 +271,7 @@ public class PythonConsoleFrontendService { prot.writeMessageEnd(); } - public java.lang.String getResult() throws org.apache.thrift.TException { + public java.lang.String getResult() throws KeyboardInterruptException, org.apache.thrift.TException { if (getState() != org.apache.thrift.async.TAsyncMethodCall.State.RESPONSE_READ) { throw new java.lang.IllegalStateException("Method call not finished!"); } @@ -479,7 +482,11 @@ public class PythonConsoleFrontendService { public requestInput_result getResult(I iface, requestInput_args args) throws org.apache.thrift.TException { requestInput_result result = new requestInput_result(); - result.success = iface.requestInput(args.path); + try { + result.success = iface.requestInput(args.path); + } catch (KeyboardInterruptException interrupted) { + result.interrupted = interrupted; + } return result; } } @@ -696,7 +703,11 @@ public class PythonConsoleFrontendService { byte msgType = org.apache.thrift.protocol.TMessageType.REPLY; org.apache.thrift.TSerializable msg; requestInput_result result = new requestInput_result(); - if (e instanceof org.apache.thrift.transport.TTransportException) { + if (e instanceof KeyboardInterruptException) { + result.interrupted = (KeyboardInterruptException) e; + result.setInterruptedIsSet(true); + msg = result; + } else if (e instanceof org.apache.thrift.transport.TTransportException) { _LOGGER.error("TTransportException inside handler", e); fb.close(); return; @@ -1946,15 +1957,18 @@ public class PythonConsoleFrontendService { private static final org.apache.thrift.protocol.TStruct STRUCT_DESC = new org.apache.thrift.protocol.TStruct("requestInput_result"); private static final org.apache.thrift.protocol.TField SUCCESS_FIELD_DESC = new org.apache.thrift.protocol.TField("success", org.apache.thrift.protocol.TType.STRING, (short)0); + private static final org.apache.thrift.protocol.TField INTERRUPTED_FIELD_DESC = new org.apache.thrift.protocol.TField("interrupted", org.apache.thrift.protocol.TType.STRUCT, (short)1); private static final org.apache.thrift.scheme.SchemeFactory STANDARD_SCHEME_FACTORY = new requestInput_resultStandardSchemeFactory(); private static final org.apache.thrift.scheme.SchemeFactory TUPLE_SCHEME_FACTORY = new requestInput_resultTupleSchemeFactory(); public java.lang.String success; // required + public KeyboardInterruptException interrupted; // required /** The set of fields this struct contains, along with convenience methods for finding and manipulating them. */ public enum _Fields implements org.apache.thrift.TFieldIdEnum { - SUCCESS((short)0, "success"); + SUCCESS((short)0, "success"), + INTERRUPTED((short)1, "interrupted"); private static final java.util.Map byName = new java.util.HashMap(); @@ -1971,6 +1985,8 @@ public class PythonConsoleFrontendService { switch(fieldId) { case 0: // SUCCESS return SUCCESS; + case 1: // INTERRUPTED + return INTERRUPTED; default: return null; } @@ -2016,6 +2032,8 @@ public class PythonConsoleFrontendService { java.util.Map<_Fields, org.apache.thrift.meta_data.FieldMetaData> tmpMap = new java.util.EnumMap<_Fields, org.apache.thrift.meta_data.FieldMetaData>(_Fields.class); tmpMap.put(_Fields.SUCCESS, new org.apache.thrift.meta_data.FieldMetaData("success", org.apache.thrift.TFieldRequirementType.DEFAULT, new org.apache.thrift.meta_data.FieldValueMetaData(org.apache.thrift.protocol.TType.STRING))); + tmpMap.put(_Fields.INTERRUPTED, new org.apache.thrift.meta_data.FieldMetaData("interrupted", org.apache.thrift.TFieldRequirementType.DEFAULT, + new org.apache.thrift.meta_data.StructMetaData(org.apache.thrift.protocol.TType.STRUCT, KeyboardInterruptException.class))); metaDataMap = java.util.Collections.unmodifiableMap(tmpMap); org.apache.thrift.meta_data.FieldMetaData.addStructMetaDataMap(requestInput_result.class, metaDataMap); } @@ -2024,10 +2042,12 @@ public class PythonConsoleFrontendService { } public requestInput_result( - java.lang.String success) + java.lang.String success, + KeyboardInterruptException interrupted) { this(); this.success = success; + this.interrupted = interrupted; } /** @@ -2037,6 +2057,9 @@ public class PythonConsoleFrontendService { if (other.isSetSuccess()) { this.success = other.success; } + if (other.isSetInterrupted()) { + this.interrupted = new KeyboardInterruptException(other.interrupted); + } } public requestInput_result deepCopy() { @@ -2046,6 +2069,7 @@ public class PythonConsoleFrontendService { @Override public void clear() { this.success = null; + this.interrupted = null; } public java.lang.String getSuccess() { @@ -2072,6 +2096,30 @@ public class PythonConsoleFrontendService { } } + public KeyboardInterruptException getInterrupted() { + return this.interrupted; + } + + public requestInput_result setInterrupted(KeyboardInterruptException interrupted) { + this.interrupted = interrupted; + return this; + } + + public void unsetInterrupted() { + this.interrupted = null; + } + + /** Returns true if field interrupted is set (has been assigned a value) and false otherwise */ + public boolean isSetInterrupted() { + return this.interrupted != null; + } + + public void setInterruptedIsSet(boolean value) { + if (!value) { + this.interrupted = null; + } + } + public void setFieldValue(_Fields field, java.lang.Object value) { switch (field) { case SUCCESS: @@ -2082,6 +2130,14 @@ public class PythonConsoleFrontendService { } break; + case INTERRUPTED: + if (value == null) { + unsetInterrupted(); + } else { + setInterrupted((KeyboardInterruptException)value); + } + break; + } } @@ -2090,6 +2146,9 @@ public class PythonConsoleFrontendService { case SUCCESS: return getSuccess(); + case INTERRUPTED: + return getInterrupted(); + } throw new java.lang.IllegalStateException(); } @@ -2103,6 +2162,8 @@ public class PythonConsoleFrontendService { switch (field) { case SUCCESS: return isSetSuccess(); + case INTERRUPTED: + return isSetInterrupted(); } throw new java.lang.IllegalStateException(); } @@ -2131,6 +2192,15 @@ public class PythonConsoleFrontendService { return false; } + boolean this_present_interrupted = true && this.isSetInterrupted(); + boolean that_present_interrupted = true && that.isSetInterrupted(); + if (this_present_interrupted || that_present_interrupted) { + if (!(this_present_interrupted && that_present_interrupted)) + return false; + if (!this.interrupted.equals(that.interrupted)) + return false; + } + return true; } @@ -2142,6 +2212,10 @@ public class PythonConsoleFrontendService { if (isSetSuccess()) hashCode = hashCode * 8191 + success.hashCode(); + hashCode = hashCode * 8191 + ((isSetInterrupted()) ? 131071 : 524287); + if (isSetInterrupted()) + hashCode = hashCode * 8191 + interrupted.hashCode(); + return hashCode; } @@ -2163,6 +2237,16 @@ public class PythonConsoleFrontendService { return lastComparison; } } + lastComparison = java.lang.Boolean.valueOf(isSetInterrupted()).compareTo(other.isSetInterrupted()); + if (lastComparison != 0) { + return lastComparison; + } + if (isSetInterrupted()) { + lastComparison = org.apache.thrift.TBaseHelper.compareTo(this.interrupted, other.interrupted); + if (lastComparison != 0) { + return lastComparison; + } + } return 0; } @@ -2190,6 +2274,14 @@ public class PythonConsoleFrontendService { sb.append(this.success); } first = false; + if (!first) sb.append(", "); + sb.append("interrupted:"); + if (this.interrupted == null) { + sb.append("null"); + } else { + sb.append(this.interrupted); + } + first = false; sb.append(")"); return sb.toString(); } @@ -2241,6 +2333,15 @@ public class PythonConsoleFrontendService { org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } break; + case 1: // INTERRUPTED + if (schemeField.type == org.apache.thrift.protocol.TType.STRUCT) { + struct.interrupted = new KeyboardInterruptException(); + struct.interrupted.read(iprot); + struct.setInterruptedIsSet(true); + } else { + org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); + } + break; default: org.apache.thrift.protocol.TProtocolUtil.skip(iprot, schemeField.type); } @@ -2261,6 +2362,11 @@ public class PythonConsoleFrontendService { oprot.writeString(struct.success); oprot.writeFieldEnd(); } + if (struct.interrupted != null) { + oprot.writeFieldBegin(INTERRUPTED_FIELD_DESC); + struct.interrupted.write(oprot); + oprot.writeFieldEnd(); + } oprot.writeFieldStop(); oprot.writeStructEnd(); } @@ -2282,20 +2388,31 @@ public class PythonConsoleFrontendService { if (struct.isSetSuccess()) { optionals.set(0); } - oprot.writeBitSet(optionals, 1); + if (struct.isSetInterrupted()) { + optionals.set(1); + } + oprot.writeBitSet(optionals, 2); if (struct.isSetSuccess()) { oprot.writeString(struct.success); } + if (struct.isSetInterrupted()) { + struct.interrupted.write(oprot); + } } @Override public void read(org.apache.thrift.protocol.TProtocol prot, requestInput_result struct) throws org.apache.thrift.TException { org.apache.thrift.protocol.TTupleProtocol iprot = (org.apache.thrift.protocol.TTupleProtocol) prot; - java.util.BitSet incoming = iprot.readBitSet(1); + java.util.BitSet incoming = iprot.readBitSet(2); if (incoming.get(0)) { struct.success = iprot.readString(); struct.setSuccessIsSet(true); } + if (incoming.get(1)) { + struct.interrupted = new KeyboardInterruptException(); + struct.interrupted.read(iprot); + struct.setInterruptedIsSet(true); + } } } diff --git a/python/helpers/pydev/_pydev_bundle/pydev_stdin.py b/python/helpers/pydev/_pydev_bundle/pydev_stdin.py index bf2a570a108d..deaab7456e93 100644 --- a/python/helpers/pydev/_pydev_bundle/pydev_stdin.py +++ b/python/helpers/pydev/_pydev_bundle/pydev_stdin.py @@ -1,5 +1,7 @@ import sys +from pydev_console.protocol import KeyboardInterruptException + # ======================================================================================================================= # BaseStdIn @@ -65,6 +67,10 @@ class StdIn(BaseStdIn): return requested_input except KeyboardInterrupt: raise # Let KeyboardInterrupt go through -- #PyDev-816: Interrupting infinite loop in the Interactive Console + except KeyboardInterruptException: + # this exception is explicitly declared in `requestInput()` method of `PythonConsoleFrontendService` Thrift service + # it is thrown on the IDE side and transferred by Thrift library as the response to `requestInput()` method + raise except: return '\n' diff --git a/python/helpers/pydev/pydev_console/console.thrift b/python/helpers/pydev/pydev_console/console.thrift index c87a3b94950d..0c186f5a41c5 100644 --- a/python/helpers/pydev/pydev_console/console.thrift +++ b/python/helpers/pydev/pydev_console/console.thrift @@ -172,10 +172,13 @@ service PythonConsoleBackendService { void loadFullValue(1: LoadFullValueRequestSeq seq, 2: list variables), } +exception KeyboardInterruptException { +} + service PythonConsoleFrontendService { void notifyFinished(1: bool needsMoreInput), - string requestInput(1: string path), + string requestInput(1: string path) throws (1: KeyboardInterruptException interrupted), void notifyAboutMagic(1: list commands, 2: bool isAutoMagic), diff --git a/python/helpers/pydev/pydev_console/protocol.py b/python/helpers/pydev/pydev_console/protocol.py index 63bd6720d217..58d4323bd873 100644 --- a/python/helpers/pydev/pydev_console/protocol.py +++ b/python/helpers/pydev/pydev_console/protocol.py @@ -16,6 +16,7 @@ ColHeader = _console_thrift.ColHeader RowHeader = _console_thrift.RowHeader UnsupportedArrayTypeException = _console_thrift.UnsupportedArrayTypeException +KeyboardInterruptException = _console_thrift.KeyboardInterruptException PythonConsoleFrontendService = _console_thrift.PythonConsoleFrontendService PythonConsoleBackendService = _console_thrift.PythonConsoleBackendService diff --git a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java index fb8dae6b2e5b..ebf87ef2fd79 100644 --- a/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java +++ b/python/src/com/jetbrains/python/console/PydevConsoleCommunication.java @@ -52,6 +52,7 @@ import static com.jetbrains.python.console.PydevConsoleCommunicationUtil.*; public abstract class PydevConsoleCommunication extends AbstractConsoleCommunication implements PyFrameAccessor { private static final Logger LOG = Logger.getInstance(PydevConsoleCommunication.class); + protected volatile boolean keyboardInterruption; /** * Input that should be sent to the server (waiting for raw_input) */ @@ -224,9 +225,10 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica myExecuting = executing; } - private Object execRequestInput() { + private Object execRequestInput() throws KeyboardInterruptException { waitingForInput = true; inputReceived = null; + keyboardInterruption = false; boolean needInput = true; //let the busy loop from execInterpreter free and enter a busy loop @@ -237,6 +239,11 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica //busy loop until we have an input while (inputReceived == null) { + if (keyboardInterruption) { + waitingForInput = false; + + throw new KeyboardInterruptException(); + } synchronized (lock) { try { lock.wait(10); @@ -450,6 +457,12 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica @Override public void interrupt() { + if (waitingForInput) { + // we do not want to forcibly `interrupt()` the `requestInput()` on the + // Python side otherwise the message queue to the IDE will be broken + keyboardInterruption = true; + return; + } try { getPythonConsoleBackendClient().interrupt(); } @@ -675,7 +688,7 @@ public abstract class PydevConsoleCommunication extends AbstractConsoleCommunica } @Override - public String requestInput(String path) { + public String requestInput(String path) throws KeyboardInterruptException { return (String)execRequestInput(); }