Слияние ветки 'python/update-frames-on-thread-stop' в '252'

This commit is contained in:
Nikita Iarychenko
2025-12-25 12:11:35 +03:00
committed by GitFlic
3 changed files with 75 additions and 4 deletions

View File

@@ -838,6 +838,7 @@ debugger.stack.frame.return.values=Return Values
debugger.stack.frame.frame.not.available=<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

View File

@@ -1,4 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
//
// Modified by Aleksandr Eliseev at 2025 as part of the OpenIDE project(https://openide.ru).
// Any modifications are available on the same license terms as the original source code.
package com.jetbrains.python.debugger;
import com.google.common.base.Strings;
@@ -47,6 +50,12 @@ import com.intellij.xdebugger.*;
import com.intellij.xdebugger.breakpoints.*;
import com.intellij.xdebugger.evaluation.XDebuggerEditorsProvider;
import com.intellij.xdebugger.frame.*;
import com.intellij.xdebugger.impl.XDebugSessionImpl;
import com.intellij.xdebugger.impl.frame.XDebugSessionProxy;
import com.intellij.xdebugger.impl.frame.XDebugSessionProxyKeeperKt;
import com.intellij.xdebugger.impl.frame.XDebugView;
import com.intellij.xdebugger.impl.frame.XFramesView;
import com.intellij.xdebugger.impl.ui.XDebugSessionTab;
import com.intellij.xdebugger.impl.ui.tree.nodes.XValueNodeImpl;
import com.intellij.xdebugger.stepping.XSmartStepIntoHandler;
import com.jetbrains.python.PyBundle;
@@ -1212,6 +1221,27 @@ public class PyDebugProcess extends XDebugProcess implements IPyDebugProcess, Pr
if (breakpoint != null) {
// Hit a breakpoint while already suspended. We have to remember it and stop on this breakpoint later.
myBreakpointHits.add(new BreakpointHitContext(breakpoint, threadInfo.getMessage(), suspendContext));
} else {
XDebugSession session = getSession();
if (!(session instanceof XDebugSessionImpl sessionImpl)) {
// If possible (it is XDebugSessionImpl, which currently is only implementation of XDebugSession),
// we want to rebuild only frames view
// But just in case, if in the future there is other implementation,
// we rebuild all debug views
session.rebuildViews();
return;
}
XDebugSessionTab tab = sessionImpl.getSessionTab();
if (tab == null) {
return;
}
XFramesView framesView = tab.getFramesView();
if (framesView == null) {
return;
}
XDebugSessionProxy sessionProxy = XDebugSessionProxyKeeperKt.asProxy(sessionImpl);
framesView
.processSessionEvent(XDebugView.SessionEvent.SETTINGS_CHANGED, sessionProxy);
}
}
}

View File

@@ -1,4 +1,7 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
//
// Modified by Aleksandr Eliseev at 2025 as part of the OpenIDE project(https://openide.ru).
// Any modifications are available on the same license terms as the original source code.
package com.jetbrains.python.debugger;
import com.intellij.openapi.application.ApplicationManager;
@@ -8,13 +11,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 +66,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;
}