mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
PY-72345 Pycharm 2024.1 Broken debug on Python 3.12-3.13
Add a processing function for new breakpoints. Merge-request: IJ-MR-152628 Merged-by: Egor Eliseev <Egor.Eliseev@jetbrains.com> (cherry picked from commit 63ebb4c7c620cf7cc3f56924619fc5adc09e25dd) IJ-MR-152628 GitOrigin-RevId: 1f26240498360aff61ff27878118b0eb841ec082
This commit is contained in:
committed by
intellij-monorepo-bot
parent
55ce70bcba
commit
b0995cd9a1
@@ -35,6 +35,10 @@ try:
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
_EVENT_ACTIONS = {
|
||||||
|
"ADD": lambda x, y: x | y,
|
||||||
|
"REMOVE": lambda x, y: x & ~y,
|
||||||
|
}
|
||||||
|
|
||||||
def _make_frame_cache_key(code):
|
def _make_frame_cache_key(code):
|
||||||
return code.co_firstlineno, code.co_name, code.co_filename
|
return code.co_firstlineno, code.co_name, code.co_filename
|
||||||
@@ -121,6 +125,10 @@ def _should_enable_line_events_for_code(frame, code, filename, info):
|
|||||||
# will match either global or some function
|
# will match either global or some function
|
||||||
if breakpoint.func_name in ('None', curr_func_name):
|
if breakpoint.func_name in ('None', curr_func_name):
|
||||||
has_breakpoint_in_frame = True
|
has_breakpoint_in_frame = True
|
||||||
|
# New breakpoint was processed -> stop tracing monitoring.events.INSTRUCTION
|
||||||
|
if getattr(breakpoint, '_not_processed', None):
|
||||||
|
breakpoint._not_processed = False
|
||||||
|
_modify_global_events(_EVENT_ACTIONS["REMOVE"], monitoring.events.INSTRUCTION)
|
||||||
break
|
break
|
||||||
|
|
||||||
# Check is f_back has a breakpoint => need register return event
|
# Check is f_back has a breakpoint => need register return event
|
||||||
@@ -200,6 +208,7 @@ def enable_pep669_monitoring():
|
|||||||
(monitoring.events.LINE, py_line_callback),
|
(monitoring.events.LINE, py_line_callback),
|
||||||
(monitoring.events.PY_RETURN, py_return_callback),
|
(monitoring.events.PY_RETURN, py_return_callback),
|
||||||
(monitoring.events.RAISE, py_raise_callback),
|
(monitoring.events.RAISE, py_raise_callback),
|
||||||
|
(monitoring.events.INSTRUCTION, instruction_callback),
|
||||||
):
|
):
|
||||||
monitoring.register_callback(DEBUGGER_ID, event_type, callback)
|
monitoring.register_callback(DEBUGGER_ID, event_type, callback)
|
||||||
|
|
||||||
@@ -208,6 +217,20 @@ def enable_pep669_monitoring():
|
|||||||
debugger.is_pep669_monitoring_enabled = True
|
debugger.is_pep669_monitoring_enabled = True
|
||||||
|
|
||||||
|
|
||||||
|
def process_new_breakpoint(breakpoint):
|
||||||
|
breakpoint._not_processed = True
|
||||||
|
_modify_global_events(_EVENT_ACTIONS["ADD"], monitoring.events.INSTRUCTION)
|
||||||
|
|
||||||
|
|
||||||
|
def _modify_global_events(action, event):
|
||||||
|
DEBUGGER_ID = monitoring.DEBUGGER_ID
|
||||||
|
if not monitoring.get_tool(DEBUGGER_ID):
|
||||||
|
return
|
||||||
|
|
||||||
|
current_events = monitoring.get_events(DEBUGGER_ID)
|
||||||
|
monitoring.set_events(DEBUGGER_ID, action(current_events, event))
|
||||||
|
|
||||||
|
|
||||||
def _enable_return_tracing(code):
|
def _enable_return_tracing(code):
|
||||||
local_events = monitoring.get_local_events(monitoring.DEBUGGER_ID, code)
|
local_events = monitoring.get_local_events(monitoring.DEBUGGER_ID, code)
|
||||||
monitoring.set_local_events(monitoring.DEBUGGER_ID, code,
|
monitoring.set_local_events(monitoring.DEBUGGER_ID, code,
|
||||||
@@ -220,6 +243,57 @@ def _enable_line_tracing(code):
|
|||||||
local_events | monitoring.events.LINE)
|
local_events | monitoring.events.LINE)
|
||||||
|
|
||||||
|
|
||||||
|
def instruction_callback(code, instruction_offset):
|
||||||
|
try:
|
||||||
|
py_db = GlobalDebuggerHolder.global_dbg
|
||||||
|
except AttributeError:
|
||||||
|
return
|
||||||
|
if py_db is None:
|
||||||
|
return monitoring.DISABLE
|
||||||
|
|
||||||
|
frame = _getframe(1)
|
||||||
|
# print('ENTER: INSTRUCTION ', code.co_filename, frame.f_lineno, code.co_name)
|
||||||
|
|
||||||
|
try:
|
||||||
|
if py_db._finish_debugging_session:
|
||||||
|
return monitoring.DISABLE
|
||||||
|
|
||||||
|
thread = get_current_thread()
|
||||||
|
|
||||||
|
if not is_thread_alive(thread):
|
||||||
|
return
|
||||||
|
|
||||||
|
frame_cache_key = _make_frame_cache_key(code)
|
||||||
|
|
||||||
|
info = _get_additional_info(thread)
|
||||||
|
pydev_step_cmd = info.pydev_step_cmd
|
||||||
|
is_stepping = pydev_step_cmd != -1
|
||||||
|
|
||||||
|
if not is_stepping and frame_cache_key in global_cache_skips:
|
||||||
|
return
|
||||||
|
|
||||||
|
abs_path_real_path_and_base = _get_abs_path_real_path_and_base_from_frame(frame)
|
||||||
|
filename = abs_path_real_path_and_base[1]
|
||||||
|
|
||||||
|
breakpoints_for_file = (py_db.breakpoints.get(filename)
|
||||||
|
or py_db.has_plugin_line_breaks)
|
||||||
|
if not breakpoints_for_file and not is_stepping:
|
||||||
|
return
|
||||||
|
|
||||||
|
if _should_enable_line_events_for_code(frame, code, filename, info):
|
||||||
|
_enable_line_tracing(code)
|
||||||
|
_enable_return_tracing(code)
|
||||||
|
except SystemExit:
|
||||||
|
return monitoring.DISABLE
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
if traceback is not None:
|
||||||
|
traceback.print_exc()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
return monitoring.DISABLE
|
||||||
|
|
||||||
|
|
||||||
def py_start_callback(code, instruction_offset):
|
def py_start_callback(code, instruction_offset):
|
||||||
try:
|
try:
|
||||||
py_db = GlobalDebuggerHolder.global_dbg
|
py_db = GlobalDebuggerHolder.global_dbg
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import pydevd_tracing
|
|||||||
from _pydev_bundle import pydev_log
|
from _pydev_bundle import pydev_log
|
||||||
from _pydev_imps._pydev_saved_modules import threading
|
from _pydev_imps._pydev_saved_modules import threading
|
||||||
from _pydevd_bundle import pydevd_traceproperty, pydevd_dont_trace, pydevd_utils
|
from _pydevd_bundle import pydevd_traceproperty, pydevd_dont_trace, pydevd_utils
|
||||||
|
from _pydevd_bundle.pydevd_pep_669_tracing import process_new_breakpoint
|
||||||
from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info
|
from _pydevd_bundle.pydevd_additional_thread_info import set_additional_thread_info
|
||||||
from _pydevd_bundle.pydevd_breakpoints import LineBreakpoint, get_exception_class
|
from _pydevd_bundle.pydevd_breakpoints import LineBreakpoint, get_exception_class
|
||||||
from _pydevd_bundle.pydevd_comm import (CMD_RUN, CMD_VERSION, CMD_LIST_THREADS,
|
from _pydevd_bundle.pydevd_comm import (CMD_RUN, CMD_VERSION, CMD_LIST_THREADS,
|
||||||
@@ -452,6 +453,10 @@ def process_net_command(py_db, cmd_id, seq, text):
|
|||||||
|
|
||||||
id_to_pybreakpoint[breakpoint_id] = breakpoint
|
id_to_pybreakpoint[breakpoint_id] = breakpoint
|
||||||
py_db.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints)
|
py_db.consolidate_breakpoints(file, id_to_pybreakpoint, breakpoints)
|
||||||
|
|
||||||
|
if py_db.is_pep669_monitoring_enabled:
|
||||||
|
process_new_breakpoint(breakpoint)
|
||||||
|
|
||||||
if py_db.plugin is not None:
|
if py_db.plugin is not None:
|
||||||
py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks()
|
py_db.has_plugin_line_breaks = py_db.plugin.has_line_breaks()
|
||||||
if py_db.has_plugin_line_breaks:
|
if py_db.has_plugin_line_breaks:
|
||||||
|
|||||||
27
python/testData/debug/test_add_breakpoint_after_run.py
Normal file
27
python/testData/debug/test_add_breakpoint_after_run.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_sum(a, b):
|
||||||
|
# Use a breakpoint here to inspect the program flow
|
||||||
|
result = a + b
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def greet(name):
|
||||||
|
# Use a breakpoint here to debug the call
|
||||||
|
print(f"Hello, {name}!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
counter = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
print("\nIteration:", counter) # You can set a breakpoint here while the loop runs
|
||||||
|
num1 = counter
|
||||||
|
num2 = counter + 1
|
||||||
|
sum_result = calculate_sum(num1, num2) # Step into this function during debugging
|
||||||
|
print(f"The sum of {num1} and {num2} is: {sum_result}")
|
||||||
|
greet("Debugger") # Also step into this function if needed
|
||||||
|
counter += 1
|
||||||
|
# Add a sleep to slow down the loop and make debugging easier
|
||||||
|
time.sleep(2)
|
||||||
@@ -1,16 +1,25 @@
|
|||||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||||
package com.jetbrains.env.debug
|
package com.jetbrains.env.debug
|
||||||
|
|
||||||
|
import com.intellij.openapi.projectRoots.Sdk
|
||||||
import com.intellij.xdebugger.XDebuggerManager
|
import com.intellij.xdebugger.XDebuggerManager
|
||||||
import com.jetbrains.env.PyEnvTestCase
|
import com.jetbrains.env.PyEnvTestCase
|
||||||
import org.junit.Test
|
import com.jetbrains.python.run.AbstractPythonRunConfiguration
|
||||||
|
import com.jetbrains.python.run.PythonRunConfiguration
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
class PythonDebuggerAggregatorTest: PyEnvTestCase() {
|
class PythonDebuggerAggregatorTest: PyEnvTestCase() {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSeveralReturnSignal() {
|
fun testSeveralReturnSignal() {
|
||||||
runPythonTest(object: PyDebuggerTask("/debug", "test_several_return_signal.py") {
|
runPythonTest(object: PyDebuggerTask("/debug", "test_several_return_signal.py") {
|
||||||
|
override fun createRunConfiguration(sdkHome: String, existingSdk: Sdk?): AbstractPythonRunConfiguration<*> {
|
||||||
|
val runConfiguration = super.createRunConfiguration(sdkHome, existingSdk) as PythonRunConfiguration
|
||||||
|
runConfiguration.envs["PYDEVD_USE_CYTHON"] = "NO"
|
||||||
|
return runConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
override fun before() {
|
override fun before() {
|
||||||
toggleBreakpoints(6)
|
toggleBreakpoints(6)
|
||||||
}
|
}
|
||||||
@@ -48,4 +57,29 @@ class PythonDebuggerAggregatorTest: PyEnvTestCase() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAddBreakpointAfterRun()
|
||||||
|
{
|
||||||
|
runPythonTest(object: PyDebuggerTask("/debug", "test_add_breakpoint_after_run.py") {
|
||||||
|
override fun createRunConfiguration(sdkHome: String, existingSdk: Sdk?): AbstractPythonRunConfiguration<*> {
|
||||||
|
val runConfiguration = super.createRunConfiguration(sdkHome, existingSdk) as PythonRunConfiguration
|
||||||
|
runConfiguration.envs["PYDEVD_USE_CYTHON"] = "NO"
|
||||||
|
return runConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before() {
|
||||||
|
setWaitForTermination(false)
|
||||||
|
toggleBreakpoints(20)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun testing() {
|
||||||
|
waitForPause()
|
||||||
|
removeBreakpoint(scriptName, 20)
|
||||||
|
resume()
|
||||||
|
toggleBreakpoints(20)
|
||||||
|
waitForPause()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user