Skip to content

Commit aabb252

Browse files
authored
Sync from debugpy
* Updates from debugpy to get debugpy tests to pass * Attach to support for 3.12/3.13 / update attach dlls * Fix case where the same line was reported twice * Support for Qt6 (pyqt and pyside)
1 parent 6cd4d43 commit aabb252

45 files changed

Lines changed: 6001 additions & 4352 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pydevd-tests-python.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ jobs:
9393
pip install psutil --no-warn-script-location
9494
pip install ipython --no-warn-script-location
9595
pip install untangle --no-warn-script-location
96+
pip install importlib-metadata --no-warn-script-location
9697
- name: Install Python 3.x deps
9798
if: contains(matrix.name, 'py3') && !contains(matrix.name, 'pypy') && !contains(matrix.name, 'py312') && !contains(matrix.name, 'py311')
9899
run: |

_pydevd_bundle/pydevd_constants.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,9 +173,10 @@ def _current_frames():
173173
IS_PY310_OR_GREATER = sys.version_info >= (3, 10)
174174
IS_PY311_OR_GREATER = sys.version_info >= (3, 11)
175175
IS_PY312_OR_GREATER = sys.version_info >= (3, 12)
176+
IS_PY313_OR_GREATER = sys.version_info >= (3, 13)
176177

177178
# Not currently supported in Python 3.12.
178-
SUPPORT_ATTACH_TO_PID = not IS_PY312_OR_GREATER
179+
SUPPORT_ATTACH_TO_PID = not IS_PY313_OR_GREATER
179180

180181

181182
def version_str(v):

_pydevd_bundle/pydevd_cython.c

Lines changed: 191 additions & 161 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

_pydevd_bundle/pydevd_cython.pyx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1374,12 +1374,12 @@ cdef class PyDBFrame:
13741374

13751375

13761376
# IFDEF CYTHON -- DONT EDIT THIS FILE (it is automatically generated)
1377-
def should_stop_on_exception(py_db, PyDBAdditionalThreadInfo info, frame, thread, arg, prev_user_uncaught_exc_info):
1377+
def should_stop_on_exception(py_db, PyDBAdditionalThreadInfo info, frame, thread, arg, prev_user_uncaught_exc_info, is_unwind=False):
13781378
cdef bint should_stop;
13791379
cdef bint was_just_raised;
13801380
cdef list check_excs;
13811381
# ELSE
1382-
# def should_stop_on_exception(py_db, info, frame, thread, arg, prev_user_uncaught_exc_info):
1382+
# def should_stop_on_exception(py_db, info, frame, thread, arg, prev_user_uncaught_exc_info, is_unwind=False):
13831383
# ENDIF
13841384

13851385
should_stop = False
@@ -1396,7 +1396,7 @@ def should_stop_on_exception(py_db, PyDBAdditionalThreadInfo info, frame, thread
13961396
exception_breakpoint = None
13971397
try:
13981398
if py_db.plugin is not None:
1399-
result = py_db.plugin.exception_break(py_db, frame, thread, arg)
1399+
result = py_db.plugin.exception_break(py_db, frame, thread, arg, is_unwind)
14001400
if result:
14011401
should_stop, frame = result
14021402
except:

_pydevd_bundle/pydevd_dont_trace_files.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
'inputhookpyglet.py',
3131
'inputhookqt4.py',
3232
'inputhookqt5.py',
33+
'inputhookqt6.py',
3334
'inputhooktk.py',
3435
'inputhookwx.py',
3536
'matplotlibtools.py',

_pydevd_bundle/pydevd_frame.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,12 +1053,12 @@ def trace_dispatch(self, frame, event, arg):
10531053

10541054

10551055
# IFDEF CYTHON
1056-
# def should_stop_on_exception(py_db, PyDBAdditionalThreadInfo info, frame, thread, arg, prev_user_uncaught_exc_info):
1056+
# def should_stop_on_exception(py_db, PyDBAdditionalThreadInfo info, frame, thread, arg, prev_user_uncaught_exc_info, is_unwind=False):
10571057
# cdef bint should_stop;
10581058
# cdef bint was_just_raised;
10591059
# cdef list check_excs;
10601060
# ELSE
1061-
def should_stop_on_exception(py_db, info, frame, thread, arg, prev_user_uncaught_exc_info):
1061+
def should_stop_on_exception(py_db, info, frame, thread, arg, prev_user_uncaught_exc_info, is_unwind=False):
10621062
# ENDIF
10631063

10641064
should_stop = False
@@ -1075,7 +1075,7 @@ def should_stop_on_exception(py_db, info, frame, thread, arg, prev_user_uncaught
10751075
exception_breakpoint = None
10761076
try:
10771077
if py_db.plugin is not None:
1078-
result = py_db.plugin.exception_break(py_db, frame, thread, arg)
1078+
result = py_db.plugin.exception_break(py_db, frame, thread, arg, is_unwind)
10791079
if result:
10801080
should_stop, frame = result
10811081
except:

_pydevd_bundle/pydevd_frame_utils.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from _pydev_bundle import pydev_log
33
import itertools
44
from typing import Any, Dict
5+
from os.path import basename, splitext
56

67

78
class Frame(object):
@@ -42,8 +43,32 @@ def remove_exception_from_frame(frame):
4243
def just_raised(trace):
4344
if trace is None:
4445
return False
46+
4547
return trace.tb_next is None
4648

49+
def short_tb(exc_tb):
50+
traceback = []
51+
while exc_tb:
52+
traceback.append('{%r, %r, %r}' % (exc_tb.tb_frame.f_code.co_filename,
53+
exc_tb.tb_frame.f_code.co_name,
54+
exc_tb.tb_lineno))
55+
exc_tb = exc_tb.tb_next
56+
return 'Traceback: %s\n' % (' -> '.join(traceback))
57+
58+
def short_frame(frame):
59+
if frame is None:
60+
return 'None'
61+
62+
filename = frame.f_code.co_filename
63+
name = splitext(basename(filename))[0]
64+
return '%s::%s %s' % (name, frame.f_code.co_name, frame.f_lineno)
65+
66+
def short_stack(frame):
67+
stack = []
68+
while frame:
69+
stack.append(short_frame(frame))
70+
frame = frame.f_back
71+
return 'Stack: %s\n' % (' -> '.join(stack))
4772

4873
def ignore_exception_trace(trace):
4974
while trace is not None:

_pydevd_bundle/pydevd_plugin_utils.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ def suspend(self, py_db, thread, frame, bp_type):
191191

192192
return None
193193

194-
def exception_break(self, py_db, frame, thread, arg):
194+
def exception_break(self, py_db, frame, thread, arg, is_unwind=False):
195195
for plugin in self.active_plugins:
196-
ret = plugin.exception_break(py_db, frame, thread, arg)
196+
ret = plugin.exception_break(py_db, frame, thread, arg, is_unwind)
197197
if ret is not None:
198198
return ret
199199

_pydevd_bundle/pydevd_safe_repr.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class SafeRepr(object):
2020
# most level, and truncated to maxstring_inner characters inside
2121
# collections.
2222
maxstring_outer = 2**16
23-
maxstring_inner = 30
23+
maxstring_inner = 128
2424
string_types = (str, bytes)
2525
bytes = bytes
2626
set_info = (set, "{", "}", False)
@@ -30,7 +30,7 @@ class SafeRepr(object):
3030

3131
# Collection types are recursively iterated for each limit in
3232
# maxcollection.
33-
maxcollection = (15, 10)
33+
maxcollection = (60, 20)
3434

3535
# Specifies type, prefix string, suffix string, and whether to include a
3636
# comma if there is only one element. (Using a sequence rather than a
@@ -61,7 +61,7 @@ class SafeRepr(object):
6161
# All other types are treated identically to strings, but using
6262
# different limits.
6363
maxother_outer = 2**16
64-
maxother_inner = 30
64+
maxother_inner = 128
6565

6666
convert_to_hex = False
6767
raw_value = False

_pydevd_sys_monitoring/_pydevd_sys_monitoring.py

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@
3232
from _pydevd_bundle.pydevd_trace_dispatch import is_unhandled_exception
3333
from _pydevd_bundle.pydevd_breakpoints import stop_on_unhandled_exception
3434
from _pydevd_bundle.pydevd_utils import get_clsname_for_code
35+
from _pydevd_bundle.pydevd_dont_trace_files import PYDEV_FILE
36+
3537

3638
# fmt: off
3739
# IFDEF CYTHON
@@ -168,7 +170,6 @@ def _get_bootstrap_frame(depth: int) -> Tuple[Optional[FrameType], bool]:
168170

169171
return f_bootstrap, is_bootstrap_frame_internal
170172

171-
172173
# fmt: off
173174
# IFDEF CYTHON
174175
# cdef _get_unhandled_exception_frame(int depth):
@@ -177,10 +178,19 @@ def _get_unhandled_exception_frame(depth: int) -> Optional[FrameType]:
177178
# ENDIF
178179
# fmt: on
179180
try:
180-
return _thread_local_info.f_unhandled
181+
result = _thread_local_info.f_unhandled
182+
183+
# Make sure the result is from the same exception. That means the result is in the stack somewhere.
184+
if result is not None:
185+
orig = frame = _getframe(depth)
186+
while result != frame and frame is not None:
187+
frame = frame.f_back
188+
if frame is not None:
189+
return result
190+
del _thread_local_info.f_unhandled
191+
raise AttributeError("Unhandled frame from different exception")
181192
except:
182-
frame = _getframe(depth)
183-
f_unhandled = frame
193+
f_unhandled = _getframe(depth)
184194

185195
while f_unhandled is not None and f_unhandled.f_back is not None:
186196
f_back = f_unhandled.f_back
@@ -205,6 +215,14 @@ def _get_unhandled_exception_frame(depth: int) -> Optional[FrameType]:
205215
if f_back.f_code.co_name.startswith(("run", "_run")):
206216
break
207217

218+
elif name == "<frozen runpy>":
219+
if f_back.f_code.co_name.startswith(("run", "_run")):
220+
break
221+
222+
elif name == "runpy":
223+
if f_back.f_code.co_name.startswith(("run", "_run")):
224+
break
225+
208226
f_unhandled = f_back
209227

210228
if f_unhandled is not None:
@@ -631,6 +649,7 @@ def _enable_line_tracing(code):
631649
# ENDIF
632650
# fmt: on
633651
# print('enable line tracing', code)
652+
_ensure_monitoring()
634653
events = monitor.get_local_events(DEBUGGER_ID, code)
635654
monitor.set_local_events(DEBUGGER_ID, code, events | monitor.events.LINE | monitor.events.JUMP)
636655

@@ -643,6 +662,7 @@ def _enable_return_tracing(code):
643662
# ENDIF
644663
# fmt: on
645664
# print('enable return tracing', code)
665+
_ensure_monitoring()
646666
events = monitor.get_local_events(DEBUGGER_ID, code)
647667
monitor.set_local_events(DEBUGGER_ID, code, events | monitor.events.PY_RETURN)
648668

@@ -654,6 +674,7 @@ def _enable_return_tracing(code):
654674
def disable_code_tracing(code):
655675
# ENDIF
656676
# fmt: on
677+
_ensure_monitoring()
657678
monitor.set_local_events(DEBUGGER_ID, code, 0)
658679

659680

@@ -810,6 +831,7 @@ def _unwind_event(code, instruction, exc):
810831
if thread_info is None:
811832
return
812833

834+
813835
py_db: object = GlobalDebuggerHolder.global_dbg
814836
if py_db is None or py_db.pydb_disposed:
815837
return
@@ -831,9 +853,10 @@ def _unwind_event(code, instruction, exc):
831853
py_db.break_on_caught_exceptions or py_db.break_on_user_uncaught_exceptions or py_db.has_plugin_exception_breaks
832854
)
833855

856+
834857
if has_caught_exception_breakpoint_in_pydb:
835858
_should_stop, frame, user_uncaught_exc_info = should_stop_on_exception(
836-
py_db, thread_info.additional_info, frame, thread_info.thread, arg, None
859+
py_db, thread_info.additional_info, frame, thread_info.thread, arg, None, is_unwind=True
837860
)
838861
if user_uncaught_exc_info:
839862
# TODO: Check: this may no longer be needed as in the unwind we know it's
@@ -842,16 +865,17 @@ def _unwind_event(code, instruction, exc):
842865
container_obj = _TryExceptContainerObj(py_db.collect_try_except_info(frame.f_code))
843866
func_code_info.try_except_container_obj = container_obj
844867

845-
if is_unhandled_exception(
868+
is_unhandled = is_unhandled_exception(
846869
func_code_info.try_except_container_obj, py_db, frame, user_uncaught_exc_info[1], user_uncaught_exc_info[2]
847-
):
848-
# print('stop in user uncaught')
870+
)
871+
872+
if is_unhandled:
849873
handle_exception(py_db, thread_info.thread, frame, user_uncaught_exc_info[0], EXCEPTION_TYPE_USER_UNHANDLED)
850874
return
851875

852876
break_on_uncaught_exceptions = py_db.break_on_uncaught_exceptions
853877
if break_on_uncaught_exceptions:
854-
if frame is _get_unhandled_exception_frame(depth=1):
878+
if frame is _get_unhandled_exception_frame(1):
855879
stop_on_unhandled_exception(py_db, thread_info.thread, thread_info.additional_info, arg)
856880
return
857881

@@ -881,7 +905,7 @@ def _raise_event(code, instruction, exc):
881905
thread_info = _get_thread_info(True, 1)
882906
if thread_info is None:
883907
return
884-
908+
885909
py_db: object = GlobalDebuggerHolder.global_dbg
886910
if py_db is None or py_db.pydb_disposed:
887911
return
@@ -895,17 +919,22 @@ def _raise_event(code, instruction, exc):
895919
if func_code_info.always_skip_code:
896920
return
897921

898-
# print('_raise_event --- ', code, exc)
899-
900922
frame = _getframe(1)
901923
arg = (type(exc), exc, exc.__traceback__)
924+
925+
# Compute the previous exception info (if any). We use it to check if the exception
926+
# should be stopped
927+
prev_exc_info = _thread_local_info._user_uncaught_exc_info if hasattr(_thread_local_info, "_user_uncaught_exc_info") else None
902928
should_stop, frame, _user_uncaught_exc_info = should_stop_on_exception(
903-
py_db, thread_info.additional_info, frame, thread_info.thread, arg, None
929+
py_db, thread_info.additional_info, frame, thread_info.thread, arg, prev_exc_info
904930
)
931+
932+
# Save the current exception info for the next raise event.
933+
_thread_local_info._user_uncaught_exc_info = _user_uncaught_exc_info
934+
905935
# print('!!!! should_stop (in raise)', should_stop)
906936
if should_stop:
907937
handle_exception(py_db, thread_info.thread, frame, arg, EXCEPTION_TYPE_HANDLED)
908-
return
909938

910939

911940
# fmt: off
@@ -1307,6 +1336,10 @@ def _jump_event(code, from_offset, to_offset):
13071336
if py_db is None or py_db.pydb_disposed:
13081337
return monitor.DISABLE
13091338

1339+
# If we get another jump event, remove the extra check for the line event
1340+
if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
1341+
del _thread_local_info.f_disable_next_line_if_match
1342+
13101343
if not thread_info.trace or thread_info.thread._is_stopped:
13111344
# For thread-related stuff we can't disable the code tracing because other
13121345
# threads may still want it...
@@ -1325,14 +1358,17 @@ def _jump_event(code, from_offset, to_offset):
13251358

13261359
from_line = func_code_info.get_line_of_offset(from_offset)
13271360
to_line = func_code_info.get_line_of_offset(to_offset)
1328-
# print('jump event', code.co_name, 'from line', from_line, 'to line', to_line)
13291361

13301362
if from_line != to_line:
13311363
# I.e.: use case: "yield from [j for j in a if j % 2 == 0]"
13321364
return monitor.DISABLE
13331365

13341366
# We know the frame depth.
13351367
frame = _getframe(1)
1368+
1369+
# Disable the next line event as we're jumping to a line. The line event will be redundant.
1370+
_thread_local_info.f_disable_next_line_if_match = (func_code_info.co_filename, frame.f_lineno)
1371+
13361372
return _internal_line_event(func_code_info, frame, frame.f_lineno)
13371373

13381374

@@ -1360,6 +1396,15 @@ def _line_event(code, line):
13601396
if py_db is None or py_db.pydb_disposed:
13611397
return monitor.DISABLE
13621398

1399+
# If we get another line event, remove the extra check for the line event
1400+
if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
1401+
(co_filename, line_to_skip) = _thread_local_info.f_disable_next_line_if_match
1402+
del _thread_local_info.f_disable_next_line_if_match
1403+
if line_to_skip is line and co_filename == code.co_filename:
1404+
# The last jump already jumped to this line and we haven't had any
1405+
# line events or jumps since then. We don't want to consider this line twice
1406+
return
1407+
13631408
if not thread_info.trace or thread_info.thread._is_stopped:
13641409
# For thread-related stuff we can't disable the code tracing because other
13651410
# threads may still want it...
@@ -1604,6 +1649,7 @@ def _start_method_event(code, instruction_offset):
16041649
# threads may still want it...
16051650
return
16061651

1652+
16071653
frame = _getframe(1)
16081654
func_code_info = _get_func_code_info(code, frame)
16091655
if func_code_info.always_skip_code:
@@ -1651,6 +1697,19 @@ def _start_method_event(code, instruction_offset):
16511697

16521698
return monitor.DISABLE
16531699

1700+
# fmt: off
1701+
# IFDEF CYTHON
1702+
# cpdef _ensure_monitoring():
1703+
# ELSE
1704+
def _ensure_monitoring():
1705+
# ENDIF
1706+
# fmt: on
1707+
DEBUGGER_ID = monitor.DEBUGGER_ID
1708+
if not monitor.get_tool(DEBUGGER_ID):
1709+
monitor.use_tool_id(DEBUGGER_ID, "pydevd")
1710+
update_monitor_events()
1711+
restart_events()
1712+
16541713

16551714
# fmt: off
16561715
# IFDEF CYTHON

0 commit comments

Comments
 (0)