Skip to content
This repository has been archived by the owner on Aug 2, 2023. It is now read-only.

#653: unhandled exceptions no longer reported twice. #671

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
62 changes: 24 additions & 38 deletions ptvsd/_vendored/pydevd/_pydev_bundle/pydev_override.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,35 @@
def overrides(method):
'''
Initially meant to be used as
Meant to be used as
class B:
@overrides(A.m1)
def m1(self):
pass
but as we want to be compatible with Jython 2.1 where decorators have an uglier syntax (needing an assign
after the method), it should now be used without being a decorator as below (in which case we don't even check
for anything, just that the parent name was actually properly loaded).
i.e.:
class B:
overrides(A.m1)
def m1(self):
pass
'''
return
def wrapper(func):
if func.__name__ != method.__name__:
msg = "Wrong @override: %r expected, but overwriting %r."
msg = msg % (func.__name__, method.__name__)
raise AssertionError(msg)

# def wrapper(func):
# if func.__name__ != method.__name__:
# msg = "Wrong @override: %r expected, but overwriting %r."
# msg = msg % (func.__name__, method.__name__)
# raise AssertionError(msg)
#
# if func.__doc__ is None:
# func.__doc__ = method.__doc__
#
# return func
#
# return wrapper
if func.__doc__ is None:
func.__doc__ = method.__doc__

return func

return wrapper

def implements(method):
return
# def wrapper(func):
# if func.__name__ != method.__name__:
# msg = "Wrong @implements: %r expected, but implementing %r."
# msg = msg % (func.__name__, method.__name__)
# raise AssertionError(msg)
#
# if func.__doc__ is None:
# func.__doc__ = method.__doc__
#
# return func
#
# return wrapper
def wrapper(func):
if func.__name__ != method.__name__:
msg = "Wrong @implements: %r expected, but implementing %r."
msg = msg % (func.__name__, method.__name__)
raise AssertionError(msg)

if func.__doc__ is None:
func.__doc__ = method.__doc__

return func

return wrapper
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class PyDBAdditionalThreadInfo(object):
# cdef public int suspend_type;
# cdef public int pydev_next_line;
# cdef public str pydev_func_name;
# cdef public bint suspended_at_unhandled;
# ELSE
__slots__ = [
'pydev_state',
Expand All @@ -93,6 +94,7 @@ class PyDBAdditionalThreadInfo(object):
'suspend_type',
'pydev_next_line',
'pydev_func_name',
'suspended_at_unhandled',
]
# ENDIF

Expand All @@ -111,6 +113,7 @@ def __init__(self):
self.suspend_type = PYTHON_SUSPEND
self.pydev_next_line = -1
self.pydev_func_name = '.invalid.' # Must match the type in cython
self.suspended_at_unhandled = False

def iter_frames(self, t):
# sys._current_frames(): dictionary with thread id -> topmost frame
Expand Down
10 changes: 5 additions & 5 deletions ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def to_xml(self):
#=======================================================================================================================
class DebugConsoleStdIn(BaseStdIn):

overrides(BaseStdIn.readline)
@overrides(BaseStdIn.readline)
def readline(self, *args, **kwargs):
sys.stderr.write('Warning: Reading from stdin is still not supported in this console.\n')
return '\n'
Expand All @@ -79,7 +79,7 @@ class DebugConsole(InteractiveConsole, BaseInterpreterInterface):
errors and outputs to the debug console
"""

overrides(BaseInterpreterInterface.create_std_in)
@overrides(BaseInterpreterInterface.create_std_in)
def create_std_in(self, *args, **kwargs):
try:
if not self.__buffer_output:
Expand All @@ -90,7 +90,7 @@ def create_std_in(self, *args, **kwargs):
return DebugConsoleStdIn() #If buffered, raw_input is not supported in this console.


overrides(InteractiveConsole.push)
@overrides(InteractiveConsole.push)
def push(self, line, frame, buffer_output=True):
"""Change built-in stdout and stderr methods by the
new custom StdMessage.
Expand Down Expand Up @@ -134,12 +134,12 @@ def push(self, line, frame, buffer_output=True):
return more, [], []


overrides(BaseInterpreterInterface.do_add_exec)
@overrides(BaseInterpreterInterface.do_add_exec)
def do_add_exec(self, line):
return InteractiveConsole.push(self, line)


overrides(InteractiveConsole.runcode)
@overrides(InteractiveConsole.runcode)
def runcode(self, code):
"""Execute a code object.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def trace_dispatch(py_db, frame, event, arg):
if name == 'threading':
if f_unhandled.f_code.co_name in ('__bootstrap', '_bootstrap'):
# We need __bootstrap_inner, not __bootstrap.
return py_db.trace_dispatch
return None

elif f_unhandled.f_code.co_name in ('__bootstrap_inner', '_bootstrap_inner'):
# Note: be careful not to use threading.currentThread to avoid creating a dummy thread.
Expand All @@ -62,6 +62,10 @@ def trace_dispatch(py_db, frame, event, arg):
break

elif name == 'pydevd':
if f_unhandled.f_code.co_name in ('run', 'main'):
# We need to get to _exec
return None

if f_unhandled.f_code.co_name == '_exec':
only_trace_for_unhandled_exceptions = True
break
Expand Down Expand Up @@ -92,7 +96,7 @@ def trace_dispatch(py_db, frame, event, arg):
thread_tracer = ThreadTracer((py_db, thread, additional_info, global_cache_skips, global_cache_frame_skips))

if f_unhandled is not None:
# print(' --> found', f_unhandled.f_code.co_name, f_unhandled.f_code.co_filename, f_unhandled.f_code.co_firstlineno)
# print(' --> found to trace unhandled', f_unhandled.f_code.co_name, f_unhandled.f_code.co_filename, f_unhandled.f_code.co_firstlineno)
if only_trace_for_unhandled_exceptions:
f_trace = thread_tracer.trace_unhandled_exceptions
else:
Expand Down Expand Up @@ -135,7 +139,9 @@ def __init__(self, pydb_frame_trace, unhandled_trace):
self._unhandled_trace = unhandled_trace

def trace_dispatch(self, frame, event, arg):
# print('PyDbFrameTraceAndUnhandledExceptionsTrace', event, frame.f_code.co_name, frame.f_code.co_filename, frame.f_code.co_firstlineno)
if event == 'exception' and arg is not None:
# print('self._unhandled_trace', self._unhandled_trace)
self._unhandled_trace(frame, event, arg)
else:
self._pydb_frame_trace(frame, event, arg)
Expand Down Expand Up @@ -171,19 +177,25 @@ def __init__(self, args):

def trace_unhandled_exceptions(self, frame, event, arg):
# Note that we ignore the frame as this tracing method should only be put in topmost frames already.
# print('trace_unhandled_exceptions', event, frame.f_code.co_name, frame.f_code.co_filename, frame.f_code.co_firstlineno)
if event == 'exception' and arg is not None:
from _pydevd_bundle.pydevd_breakpoints import stop_on_unhandled_exception
py_db, t, additional_info = self._args[0:3]
if arg is not None:
exctype, value, tb = arg
stop_on_unhandled_exception(py_db, t, additional_info, exctype, value, tb)
if not additional_info.suspended_at_unhandled:
if frame.f_back is not None:
additional_info.suspended_at_unhandled = True

exctype, value, tb = arg
stop_on_unhandled_exception(py_db, t, additional_info, exctype, value, tb)
# IFDEF CYTHON
# return SafeCallWrapper(self.trace_unhandled_exceptions)
# ELSE
return self.trace_unhandled_exceptions
# ENDIF

def trace_dispatch_and_unhandled_exceptions(self, frame, event, arg):
# print('trace_dispatch_and_unhandled_exceptions', event, frame.f_code.co_name, frame.f_code.co_filename, frame.f_code.co_firstlineno)
if event == 'exception' and arg is not None:
self.trace_unhandled_exceptions(frame, event, arg)
ret = self.trace_dispatch_and_unhandled_exceptions
Expand All @@ -197,7 +209,7 @@ def trace_dispatch_and_unhandled_exceptions(self, frame, event, arg):
# Ok, this frame needs to be traced and needs to deal with unhandled exceptions. Create
# a class which does this for us.
py_db_frame_trace_and_unhandled_exceptions_trace = PyDbFrameTraceAndUnhandledExceptionsTrace(
self.trace_dispatch_and_unhandled_exceptions, pydb_frame_trace)
pydb_frame_trace, self.trace_dispatch_and_unhandled_exceptions)
ret = py_db_frame_trace_and_unhandled_exceptions_trace.trace_dispatch
# IFDEF CYTHON
# return SafeCallWrapper(ret)
Expand Down
19 changes: 12 additions & 7 deletions ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,13 +137,18 @@ def find_frame(thread_id, frame_id):
else:
msgFrames += ' - '

errMsg = '''find_frame: frame not found.
Looking for thread_id:%s, frame_id:%s
Current thread_id:%s, available frames:
%s\n
''' % (thread_id, lookingFor, curr_thread_id, msgFrames)

sys.stderr.write(errMsg)
# Note: commented this error message out (it may commonly happen
# if a message asking for a frame is issued while a thread is paused
# but the thread starts running before the message is actually
# handled).
# Leaving code to uncomment during tests.
# err_msg = '''find_frame: frame not found.
# Looking for thread_id:%s, frame_id:%s
# Current thread_id:%s, available frames:
# %s\n
# ''' % (thread_id, lookingFor, curr_thread_id, msgFrames)
#
# sys.stderr.write(err_msg)
return None

return frameFound
Expand Down
34 changes: 34 additions & 0 deletions ptvsd/_vendored/pydevd/pydevd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,40 @@ def enable_qt_support(qt_support_mode):
pydev_monkey_qt.patch_qt(qt_support_mode)


def dump_threads(stream=None):
'''
Helper to dump thread info.
'''
if stream is None:
stream = sys.stderr
thread_id_to_name = {}
try:
for t in threading.enumerate():
thread_id_to_name[t.ident] = '%s (daemon: %s, pydevd thread: %s)' % (
t.name, t.daemon, getattr(t, 'is_pydev_daemon_thread', False))
except:
pass

stack_trace = [
'===============================================================================',
'Threads running',
'================================= Thread Dump =================================']

for thread_id, stack in sys._current_frames().items():
stack_trace.append('\n-------------------------------------------------------------------------------')
stack_trace.append(" Thread %s" % thread_id_to_name.get(thread_id, thread_id))
stack_trace.append('')

if 'self' in stack.f_locals:
stream.write(str(stack.f_locals['self']) + '\n')

for filename, lineno, name, line in traceback.extract_stack(stack):
stack_trace.append(' File "%s", line %d, in %s' % (filename, lineno, name))
if line:
stack_trace.append(" %s" % (line.strip()))
stack_trace.append('\n=============================== END Thread Dump ===============================')
stream.write('\n'.join(stack_trace))


def usage(doExit=0):
sys.stdout.write('Usage:\n')
Expand Down
Loading