diff --git a/python/pluginResources/messages/PyBundle.properties b/python/pluginResources/messages/PyBundle.properties index 633bb2855df0..d3751ee46c76 100644 --- a/python/pluginResources/messages/PyBundle.properties +++ b/python/pluginResources/messages/PyBundle.properties @@ -838,6 +838,7 @@ debugger.stack.frame.return.values=Return Values debugger.stack.frame.frame.not.available= debugger.stack.frame.unable.to.display.frame.variables=Unable to display frame variables debugger.stack.frames.not.available.in.non.suspended.state=Frames not available in non-suspended state +debugger.stack.frames.thread.is.sleeping.now.will.be.stopped.after.end.of.sleep=Thread is sleeping now, will be stopped after end of sleep debugger.test.failed.caption=Test failed debugger.error.in.test.setup.or.teardown.caption=Error in test set up or tear down debugger.remote.port.out.of.boundaries=The port number is out of boundaries diff --git a/python/src/com/jetbrains/python/debugger/PyExecutionStack.java b/python/src/com/jetbrains/python/debugger/PyExecutionStack.java index 907ecb98ebb5..20ffe236d7d6 100644 --- a/python/src/com/jetbrains/python/debugger/PyExecutionStack.java +++ b/python/src/com/jetbrains/python/debugger/PyExecutionStack.java @@ -8,13 +8,31 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; +import java.util.*; public class PyExecutionStack extends XExecutionStack { + private static final String IS_ON_SLEEP_FUNCTION_NAME = "__OPENIDE_INTERNAL_is_on_sleep"; + private static final String IS_ON_SLEEP_FUNCTION_DEFINITION = """ + def %s(thread_name): + try: + import sys + import threading + [my_native_id, *_] = [t.native_id for t in threading.enumerate() if t.name == thread_name] + [None] + if not my_native_id: + return False + frame = sys._current_frames()[my_native_id] + if not frame: + return False + filename = frame.f_code.co_filename + lineno = frame.f_lineno + with open(filename, 'r') as f: + return 'sleep' in f.readlines()[lineno - 1] + except BaseException: + return False + """.formatted(IS_ON_SLEEP_FUNCTION_NAME); + private final PyDebugProcess myDebugProcess; private final PyThreadInfo myThreadInfo; private PyStackFrame myTopFrame; @@ -45,7 +63,26 @@ public class PyExecutionStack extends XExecutionStack { @Override public void computeStackFrames(int firstFrameIndex, XStackFrameContainer container) { if (myThreadInfo.getState() != PyThreadInfo.State.SUSPENDED) { - container.errorOccurred(PyBundle.message("debugger.stack.frames.not.available.in.non.suspended.state")); + boolean isWaitingToStop = false; + if (myDebugProcess.isSuspendedOnAllThreadsPolicy()) { + try { + // Define function + myDebugProcess.evaluate(IS_ON_SLEEP_FUNCTION_DEFINITION, true, false); + // Call function + var isOnSleep = myDebugProcess.evaluate(IS_ON_SLEEP_FUNCTION_NAME + "('" + myThreadInfo.getName() + "')", false, false); + isWaitingToStop = Objects.equals(isOnSleep.myValue, "True"); + // Delete function + myDebugProcess.evaluate("del " + IS_ON_SLEEP_FUNCTION_NAME, true, true); + } catch (PyDebuggerException e) { + // Ignore + } + } + + if (!isWaitingToStop) { + container.errorOccurred(PyBundle.message("debugger.stack.frames.not.available.in.non.suspended.state")); + } else { + container.errorOccurred(PyBundle.message("debugger.stack.frames.thread.is.sleeping.now.will.be.stopped.after.end.of.sleep")); + } return; }