Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates from debugpy to get debugpy tests to pass #1

Draft
wants to merge 38 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
6da7d9f
Updates from debugpy to get debugpy tests to pass
rchiodo Sep 17, 2024
02c1825
Remove logging
rchiodo Sep 17, 2024
6a12743
Update repr tests
rchiodo Sep 18, 2024
dfa917e
Fix last test failure, update cython output
rchiodo Sep 18, 2024
9d57c97
Put back unchanged modules
rchiodo Sep 18, 2024
45bd5e9
Update dlls after pr build in debugpy
rchiodo Sep 23, 2024
d87ccff
Merge remote-tracking branch 'upstream/main' into rchiodo/debugpy_upd…
rchiodo Oct 7, 2024
46e6092
Regenerate cython
rchiodo Oct 7, 2024
895c855
Remove some of the unnecessary logging
rchiodo Oct 8, 2024
f39c6ca
Remove just_raised logic, was failing a test
rchiodo Oct 8, 2024
06edbb3
Fix django problem a different way
rchiodo Oct 8, 2024
202499c
Forgot jinja plugin
rchiodo Oct 8, 2024
c23b513
Add django test that matches failure case in debugpy
rchiodo Oct 8, 2024
3778455
Add tests to verify changes
rchiodo Oct 9, 2024
7f808d4
Fix test for 3.12 and add more restrictions on jump/line check
rchiodo Oct 10, 2024
597018b
Remove extra logging and special cases in tests for 3_12
rchiodo Oct 10, 2024
3c9f6fa
Put back version for ipython
rchiodo Oct 10, 2024
a906bcc
Fix cython bits
rchiodo Oct 10, 2024
2665bca
Remove unnecessary file
rchiodo Oct 10, 2024
00defa9
Turn cython back on
rchiodo Oct 10, 2024
e29f955
Remove some more unnecessary logging
rchiodo Oct 10, 2024
4e4ee8f
Fix default argument for should_stop_on_exception in Cython
rchiodo Oct 11, 2024
873321b
Get test like debugpy has
rchiodo Oct 11, 2024
75bdfdf
Switch test to attach only
rchiodo Oct 11, 2024
e13e2cb
Skip flakey test on 3_12
rchiodo Oct 11, 2024
83f9bc5
Skip new test on pypy
rchiodo Oct 11, 2024
0ee463b
Disabling the flakey test
rchiodo Oct 11, 2024
83d21c5
Review feedback and update logic for f_unhandled cache
rchiodo Oct 14, 2024
faecb6b
Fix string problem and generate test for it
rchiodo Oct 15, 2024
b9b2626
Remove extra logging
rchiodo Oct 15, 2024
2cdcb7d
Merge branch 'rchiodo/debugpy_updates' of https://github.com/rchiodo/…
rchiodo Oct 15, 2024
36e11f0
Update cython files
rchiodo Oct 15, 2024
d0e781e
Weird difference in print for new test?
rchiodo Oct 15, 2024
c484988
Add importlib-metadata as dependency of tests
rchiodo Oct 15, 2024
f933c81
Remove unnecessary variable and make test reflect what it's testing
rchiodo Oct 15, 2024
7590236
Update cython output
rchiodo Oct 15, 2024
872a0d5
Make new test retry
rchiodo Oct 15, 2024
ff3e742
Remove unnecessary get_file_type check
rchiodo Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,189 changes: 1,620 additions & 1,569 deletions _pydevd_bundle/pydevd_cython.c

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion _pydevd_bundle/pydevd_cython.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1416,7 +1416,7 @@ def should_stop_on_exception(py_db, PyDBAdditionalThreadInfo info, frame, thread
pass

else:
was_just_raised = trace.tb_next is None
was_just_raised = just_raised(trace)

# It was not handled by any plugin, lets check exception breakpoints.
check_excs = []
Expand Down
1 change: 1 addition & 0 deletions _pydevd_bundle/pydevd_dont_trace_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
'inputhookpyglet.py',
'inputhookqt4.py',
'inputhookqt5.py',
'inputhookqt6.py',
'inputhooktk.py',
'inputhookwx.py',
'matplotlibtools.py',
Expand Down
2 changes: 1 addition & 1 deletion _pydevd_bundle/pydevd_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -1095,7 +1095,7 @@ def should_stop_on_exception(py_db, info, frame, thread, arg, prev_user_uncaught
pass

else:
was_just_raised = trace.tb_next is None
was_just_raised = just_raised(trace)

# It was not handled by any plugin, lets check exception breakpoints.
check_excs = []
Expand Down
26 changes: 26 additions & 0 deletions _pydevd_bundle/pydevd_frame_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from _pydev_bundle import pydev_log
import itertools
from typing import Any, Dict
import threading
from os.path import basename, splitext


class Frame(object):
Expand Down Expand Up @@ -39,11 +41,35 @@ def remove_exception_from_frame(frame):
FILES_WITH_IMPORT_HOOKS = ["pydev_monkey_qt.py", "pydev_import_hook.py"]


_thread_local_info = threading.local()
def flag_as_unwinding(trace):
_thread_local_info._unwinding_trace = trace

def just_raised(trace):
if trace is None:
return False

if hasattr(_thread_local_info, "_unwinding_trace") and _thread_local_info._unwinding_trace is trace:
return False

return trace.tb_next is None

def short_tb(exc_type, exc_value, exc_tb):
traceback = []
while exc_tb:
traceback.append('{%r, %r, %r}' % (exc_tb.tb_frame.f_code.co_filename,
exc_tb.tb_frame.f_code.co_name,
exc_tb.tb_lineno))
exc_tb = exc_tb.tb_next
return 'Traceback: %s\nError: %s %r\n' % (' -> '.join(traceback), exc_type.__name__, str(exc_value))

def short_frame(frame):
if frame is None:
return 'None'

filename = frame.f_code.co_filename
name = splitext(basename(filename))[0]
return '%s::%s %s' % (name, frame.f_code.co_name, frame.f_lineno)

def ignore_exception_trace(trace):
while trace is not None:
Expand Down
6 changes: 3 additions & 3 deletions _pydevd_bundle/pydevd_safe_repr.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class SafeRepr(object):
# most level, and truncated to maxstring_inner characters inside
# collections.
maxstring_outer = 2**16
maxstring_inner = 30
maxstring_inner = 128
string_types = (str, bytes)
bytes = bytes
set_info = (set, "{", "}", False)
Expand All @@ -30,7 +30,7 @@ class SafeRepr(object):

# Collection types are recursively iterated for each limit in
# maxcollection.
maxcollection = (15, 10)
maxcollection = (60, 20)

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

convert_to_hex = False
raw_value = False
Expand Down
149 changes: 104 additions & 45 deletions _pydevd_sys_monitoring/_pydevd_sys_monitoring.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
RETURN_VALUES_DICT,
PYTHON_SUSPEND,
)
from _pydevd_bundle.pydevd_frame_utils import short_tb, flag_as_unwinding, short_frame
from pydevd_file_utils import (
NORM_PATHS_AND_BASE_CONTAINER,
get_abs_path_real_path_and_base_from_file,
Expand Down Expand Up @@ -168,51 +169,67 @@ def _get_bootstrap_frame(depth: int) -> Tuple[Optional[FrameType], bool]:

return f_bootstrap, is_bootstrap_frame_internal


# fmt: off
# IFDEF CYTHON
# cdef _get_unhandled_exception_frame(int depth):
# cdef _is_user_frame(frame: FrameType):
# ELSE
def _get_unhandled_exception_frame(depth: int) -> Optional[FrameType]:
def _is_user_frame(frame: FrameType) -> bool:
# ENDIF
# fmt: on
try:
return _thread_local_info.f_unhandled
except:
frame = _getframe(depth)
f_unhandled = frame
if frame is None:
return False

while f_unhandled is not None and f_unhandled.f_back is not None:
f_back = f_unhandled.f_back
filename = f_back.f_code.co_filename
name = splitext(basename(filename))[0]
filename = frame.f_code.co_filename
name = splitext(basename(filename))[0]

# When the back frame is the bootstrap (or if we have no back
# frame) then use this frame as the one to track.
if name == "threading":
if f_back.f_code.co_name in ("__bootstrap", "_bootstrap", "__bootstrap_inner", "_bootstrap_inner", "run"):
break
# When the frame is the bootstrap it is not a user frame.
if name == "threading":
if frame.f_code.co_name in ("__bootstrap", "_bootstrap", "__bootstrap_inner", "_bootstrap_inner", "run"):
return False

elif name == "pydev_monkey":
if f_back.f_code.co_name == "__call__":
break
elif name == "pydev_monkey":
if frame.f_code.co_name == "__call__":
return False

elif name == "pydevd":
if f_back.f_code.co_name in ("_exec", "run", "main"):
break
elif name == "pydevd":
if frame.f_code.co_name in ("_exec", "run", "main"):
return False

elif name == "pydevd_runpy":
if f_back.f_code.co_name.startswith(("run", "_run")):
break
elif name == "pydevd_runpy":
if frame.f_code.co_name.startswith(("run", "_run")):
return False

f_unhandled = f_back
elif filename == "<frozen runpy>":
if frame.f_code.co_name.startswith(("run", "_run")):
return False

if f_unhandled is not None:
_thread_local_info.f_unhandled = f_unhandled
return _thread_local_info.f_unhandled
elif name == 'runpy':
if frame.f_code.co_name.startswith(("run", "_run")):
return False

return f_unhandled
return True

# fmt: off
# IFDEF CYTHON
# cdef _is_last_user_frame(frame: FrameType):
# ELSE
def _is_last_user_frame(frame: FrameType) -> bool:
# ENDIF
# fmt: on
# If this frame is not a user frame, then it can't be the last one
if not _is_user_frame(frame):
return False

# If this frame is the last frame, then it is the last one

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is the best. 😄

if frame.f_back is None:
return True

# If the next frame is not a user frame, then this frame is the last one
if not _is_user_frame(frame.f_back):
return True

# Otherwise if the next frame is a user frame, then this frame is not the last one
return False

# fmt: off
# IFDEF CYTHON
Expand Down Expand Up @@ -631,6 +648,7 @@ def _enable_line_tracing(code):
# ENDIF
# fmt: on
# print('enable line tracing', code)
_ensure_monitoring()
events = monitor.get_local_events(DEBUGGER_ID, code)
monitor.set_local_events(DEBUGGER_ID, code, events | monitor.events.LINE | monitor.events.JUMP)

Expand All @@ -643,6 +661,7 @@ def _enable_return_tracing(code):
# ENDIF
# fmt: on
# print('enable return tracing', code)
_ensure_monitoring()
events = monitor.get_local_events(DEBUGGER_ID, code)
monitor.set_local_events(DEBUGGER_ID, code, events | monitor.events.PY_RETURN)

Expand All @@ -654,6 +673,7 @@ def _enable_return_tracing(code):
def disable_code_tracing(code):
# ENDIF
# fmt: on
_ensure_monitoring()
monitor.set_local_events(DEBUGGER_ID, code, 0)


Expand Down Expand Up @@ -810,6 +830,9 @@ def _unwind_event(code, instruction, exc):
if thread_info is None:
return

frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)

py_db: object = GlobalDebuggerHolder.global_dbg
if py_db is None or py_db.pydb_disposed:
return
Expand All @@ -818,19 +841,18 @@ def _unwind_event(code, instruction, exc):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return

func_code_info: FuncCodeInfo = _get_func_code_info(code, 1)
if func_code_info.always_skip_code:
return

# print('_unwind_event', code, exc)
frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)

has_caught_exception_breakpoint_in_pydb = (
py_db.break_on_caught_exceptions or py_db.break_on_user_uncaught_exceptions or py_db.has_plugin_exception_breaks
)


if has_caught_exception_breakpoint_in_pydb:
_should_stop, frame, user_uncaught_exc_info = should_stop_on_exception(
py_db, thread_info.additional_info, frame, thread_info.thread, arg, None
Expand All @@ -842,18 +864,18 @@ def _unwind_event(code, instruction, exc):
container_obj = _TryExceptContainerObj(py_db.collect_try_except_info(frame.f_code))
func_code_info.try_except_container_obj = container_obj

if is_unhandled_exception(
is_unhandled = is_unhandled_exception(
func_code_info.try_except_container_obj, py_db, frame, user_uncaught_exc_info[1], user_uncaught_exc_info[2]
):
)

if is_unhandled:
# print('stop in user uncaught')
handle_exception(py_db, thread_info.thread, frame, user_uncaught_exc_info[0], EXCEPTION_TYPE_USER_UNHANDLED)
return

break_on_uncaught_exceptions = py_db.break_on_uncaught_exceptions
if break_on_uncaught_exceptions:
if frame is _get_unhandled_exception_frame(depth=1):
stop_on_unhandled_exception(py_db, thread_info.thread, thread_info.additional_info, arg)
return
if break_on_uncaught_exceptions and _is_last_user_frame(frame):
stop_on_unhandled_exception(py_db, thread_info.thread, thread_info.additional_info, arg)


# fmt: off
Expand Down Expand Up @@ -882,6 +904,9 @@ def _raise_event(code, instruction, exc):
if thread_info is None:
return

frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)

py_db: object = GlobalDebuggerHolder.global_dbg
if py_db is None or py_db.pydb_disposed:
return
Expand All @@ -897,15 +922,23 @@ def _raise_event(code, instruction, exc):

# print('_raise_event --- ', code, exc)

frame = _getframe(1)
arg = (type(exc), exc, exc.__traceback__)
# Compute the previous exception info (if any). We use it to check if the exception
# should be stopped
prev_exc_info = _thread_local_info._user_uncaught_exc_info if hasattr(_thread_local_info, "_user_uncaught_exc_info") else None
should_stop, frame, _user_uncaught_exc_info = should_stop_on_exception(
py_db, thread_info.additional_info, frame, thread_info.thread, arg, None
py_db, thread_info.additional_info, frame, thread_info.thread, arg, prev_exc_info
)

# Save the current exception info for the next raise event.
_thread_local_info._user_uncaught_exc_info = _user_uncaught_exc_info

# print('!!!! should_stop (in raise)', should_stop)
if should_stop:
handle_exception(py_db, thread_info.thread, frame, arg, EXCEPTION_TYPE_HANDLED)
return

# Once we leave the raise event, we are no longer in the state of 'just_raised', so
# indicate that this traceback is for an exception in the unwinding state
flag_as_unwinding(exc.__traceback__)


# fmt: off
Expand Down Expand Up @@ -1333,6 +1366,10 @@ def _jump_event(code, from_offset, to_offset):

# We know the frame depth.
frame = _getframe(1)

# Disable the next line event as we're jumping to a line. The line event will be redundant.
_thread_local_info.f_disable_next_line_if_match = frame.f_lineno

return _internal_line_event(func_code_info, frame, frame.f_lineno)


Expand Down Expand Up @@ -1364,6 +1401,14 @@ def _line_event(code, line):
# For thread-related stuff we can't disable the code tracing because other
# threads may still want it...
return

if hasattr(_thread_local_info, "f_disable_next_line_if_match"):
if _thread_local_info.f_disable_next_line_if_match is line:
# If we're in a jump, we should skip this line event. The jump would have
# been considered a line event for this same line and we don't want to
# stop twice.
del _thread_local_info.f_disable_next_line_if_match
return

func_code_info: FuncCodeInfo = _get_func_code_info(code, 1)
if func_code_info.always_skip_code or func_code_info.always_filtered_out:
Expand Down Expand Up @@ -1604,6 +1649,7 @@ def _start_method_event(code, instruction_offset):
# threads may still want it...
return


frame = _getframe(1)
func_code_info = _get_func_code_info(code, frame)
if func_code_info.always_skip_code:
Expand Down Expand Up @@ -1651,6 +1697,19 @@ def _start_method_event(code, instruction_offset):

return monitor.DISABLE

# fmt: off
# IFDEF CYTHON
# cpdef _ensure_monitoring():
# ELSE
def _ensure_monitoring():
# ENDIF
# fmt: on
DEBUGGER_ID = monitor.DEBUGGER_ID
if not monitor.get_tool(DEBUGGER_ID):
monitor.use_tool_id(DEBUGGER_ID, "pydevd")
update_monitor_events()
restart_events()


# fmt: off
# IFDEF CYTHON
Expand Down
Loading
Loading