mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-13 15:52:01 +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:
|
||||
pass
|
||||
|
||||
_EVENT_ACTIONS = {
|
||||
"ADD": lambda x, y: x | y,
|
||||
"REMOVE": lambda x, y: x & ~y,
|
||||
}
|
||||
|
||||
def _make_frame_cache_key(code):
|
||||
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
|
||||
if breakpoint.func_name in ('None', curr_func_name):
|
||||
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
|
||||
|
||||
# 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.PY_RETURN, py_return_callback),
|
||||
(monitoring.events.RAISE, py_raise_callback),
|
||||
(monitoring.events.INSTRUCTION, instruction_callback),
|
||||
):
|
||||
monitoring.register_callback(DEBUGGER_ID, event_type, callback)
|
||||
|
||||
@@ -208,6 +217,20 @@ def enable_pep669_monitoring():
|
||||
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):
|
||||
local_events = monitoring.get_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)
|
||||
|
||||
|
||||
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):
|
||||
try:
|
||||
py_db = GlobalDebuggerHolder.global_dbg
|
||||
|
||||
@@ -8,6 +8,7 @@ import pydevd_tracing
|
||||
from _pydev_bundle import pydev_log
|
||||
from _pydev_imps._pydev_saved_modules import threading
|
||||
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_breakpoints import LineBreakpoint, get_exception_class
|
||||
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
|
||||
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:
|
||||
py_db.has_plugin_line_breaks = py_db.plugin.has_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.
|
||||
package com.jetbrains.env.debug
|
||||
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
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.Test
|
||||
|
||||
class PythonDebuggerAggregatorTest: PyEnvTestCase() {
|
||||
|
||||
@Test
|
||||
fun testSeveralReturnSignal() {
|
||||
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() {
|
||||
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