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

Commit

Permalink
#653: unhandled exceptions no longer reported twice.
Browse files Browse the repository at this point in the history
  • Loading branch information
fabioz committed Jul 17, 2018
1 parent 475a2d1 commit 225bb96
Show file tree
Hide file tree
Showing 13 changed files with 541 additions and 106 deletions.
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

0 comments on commit 225bb96

Please sign in to comment.