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

Commit

Permalink
Fixes issues dealing with handled and unhandled exceptions (#114, #446)…
Browse files Browse the repository at this point in the history
…. (#471)

* Fixes issues dealing with handled and unhandled exceptions (#114, #466).

* Add command to set project root directories.
  • Loading branch information
fabioz authored and karthiknadig committed Jun 12, 2018
1 parent 617727e commit d635e22
Show file tree
Hide file tree
Showing 20 changed files with 13,395 additions and 9,127 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from IPython.core import release

from _pydev_bundle.pydev_imports import xmlrpclib
from _pydevd_bundle.pydevd_constants import dict_keys

default_pydev_banner_parts = default_banner_parts

Expand Down
109 changes: 22 additions & 87 deletions ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_breakpoints.py
Original file line number Diff line number Diff line change
@@ -1,30 +1,19 @@
from _pydevd_bundle.pydevd_constants import dict_iter_values, IS_PY24
import pydevd_tracing
import sys
import threading
from _pydev_bundle import pydev_log
from _pydevd_bundle import pydevd_import_class
from _pydevd_bundle.pydevd_frame_utils import add_exception_to_frame

_original_excepthook = None
_handle_exceptions = None


from _pydev_imps._pydev_saved_modules import threading

threadingCurrentThread = threading.currentThread

from _pydevd_bundle.pydevd_comm import get_global_debugger

class ExceptionBreakpoint:
class ExceptionBreakpoint(object):

def __init__(
self,
qname,
condition,
expression,
notify_always,
notify_on_terminate,
notify_on_handled_exceptions,
notify_on_unhandled_exceptions,
notify_on_first_raise_only,
ignore_libraries
):
Expand All @@ -37,14 +26,13 @@ def __init__(

self.condition = condition
self.expression = expression
self.notify_on_terminate = notify_on_terminate
self.notify_always = notify_always
self.notify_on_unhandled_exceptions = notify_on_unhandled_exceptions
self.notify_on_handled_exceptions = notify_on_handled_exceptions
self.notify_on_first_raise_only = notify_on_first_raise_only
self.ignore_libraries = ignore_libraries

self.type = exctype


def __str__(self):
return self.qname

Expand All @@ -55,7 +43,9 @@ def has_condition(self):
def handle_hit_condition(self, frame):
return False


class LineBreakpoint(object):

def __init__(self, line, condition, func_name, expression, suspend_policy="NONE", hit_condition=None, is_logpoint=False):
self.line = line
self.condition = condition
Expand Down Expand Up @@ -92,6 +82,7 @@ def get_exception_full_qname(exctype):
return None
return str(exctype.__module__) + '.' + exctype.__name__


def get_exception_name(exctype):
if not exctype:
return None
Expand All @@ -113,115 +104,59 @@ def get_exception_breakpoint(exctype, exceptions):
return exc


def _set_additional_info_if_needed(thread):
try:
additional_info = thread.additional_info
if additional_info is None:
raise AttributeError()
except:
from _pydevd_bundle.pydevd_additional_thread_info import PyDBAdditionalThreadInfo
thread.additional_info = PyDBAdditionalThreadInfo()


#=======================================================================================================================
# _excepthook
#=======================================================================================================================
def _excepthook(exctype, value, tb):
def stop_on_unhandled_exception(py_db, thread, additional_info, exctype, value, tb):
from _pydevd_bundle.pydevd_frame import handle_breakpoint_condition, handle_breakpoint_expression
global _handle_exceptions
if _handle_exceptions:
exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions)
break_on_uncaught_exceptions = py_db.break_on_uncaught_exceptions
if break_on_uncaught_exceptions:
exception_breakpoint = get_exception_breakpoint(exctype, break_on_uncaught_exceptions)
else:
exception_breakpoint = None

#Always call the original excepthook before going on to call the debugger post mortem to show it.
_original_excepthook(exctype, value, tb)

if not exception_breakpoint:
return

if tb is None: #sometimes it can be None, e.g. with GTK
if tb is None: # sometimes it can be None, e.g. with GTK
return

if exctype is KeyboardInterrupt:
return

frames = []
debugger = get_global_debugger()
user_frame = None

while tb:
frame = tb.tb_frame
if exception_breakpoint.ignore_libraries and not debugger.not_in_scope(frame.f_code.co_filename):
if exception_breakpoint.ignore_libraries and py_db.in_project_scope(frame.f_code.co_filename):
user_frame = tb.tb_frame
frames.append(tb.tb_frame)
tb = tb.tb_next

thread = threadingCurrentThread()
frames_byid = dict([(id(frame),frame) for frame in frames])
frames_byid = dict([(id(frame), frame) for frame in frames])
if exception_breakpoint.ignore_libraries and user_frame is not None:
frame = user_frame
else:
frame = frames[-1]
exception = (exctype, value, tb)
_set_additional_info_if_needed(thread)

info = thread.additional_info
add_exception_to_frame(frame, exception)
if exception_breakpoint.condition is not None:
eval_result = handle_breakpoint_condition(debugger, info, exception_breakpoint, frame)
eval_result = handle_breakpoint_condition(py_db, additional_info, exception_breakpoint, frame)
if not eval_result:
return

if exception_breakpoint.expression is not None:
handle_breakpoint_expression(exception_breakpoint, info, frame)
handle_breakpoint_expression(exception_breakpoint, additional_info, frame)

try:
thread.additional_info.pydev_message = exception_breakpoint.qname
additional_info.pydev_message = exception_breakpoint.qname
except:
thread.additional_info.pydev_message = exception_breakpoint.qname.encode('utf-8')

pydevd_tracing.SetTrace(None) #no tracing from here

pydev_log.debug('Handling post-mortem stop on exception breakpoint %s' % exception_breakpoint.qname)

debugger.handle_post_mortem_stop(thread, frame, frames_byid, exception)
additional_info.pydev_message = exception_breakpoint.qname.encode('utf-8')

#=======================================================================================================================
# _set_pm_excepthook
#=======================================================================================================================
def _set_pm_excepthook(handle_exceptions_dict=None):
'''
Should be called to register the excepthook to be used.
pydev_log.debug('Handling post-mortem stop on exception breakpoint %s' % (exception_breakpoint.qname,))

It's only useful for uncaught exceptions. I.e.: exceptions that go up to the excepthook.
py_db.stop_on_unhandled_exception(thread, frame, frames_byid, exception)

@param handle_exceptions: dict(exception -> ExceptionBreakpoint)
The exceptions that should be handled.
'''
global _handle_exceptions
global _original_excepthook
if sys.excepthook != _excepthook:
#Only keep the original if it's not our own _excepthook (if called many times).
_original_excepthook = sys.excepthook

_handle_exceptions = handle_exceptions_dict
sys.excepthook = _excepthook

def _restore_pm_excepthook():
global _original_excepthook
if _original_excepthook:
sys.excepthook = _original_excepthook
_original_excepthook = None


def update_exception_hook(dbg):
if dbg.break_on_uncaught_exceptions:
_set_pm_excepthook(dbg.break_on_uncaught_exceptions)
else:
_restore_pm_excepthook()

def _get_class( kls ):
def _get_class(kls):
if IS_PY24 and "BaseException" == kls:
kls = "Exception"

Expand Down
11 changes: 6 additions & 5 deletions ptvsd/_vendored/pydevd/_pydevd_bundle/pydevd_comm.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ def unquote(s):

CMD_REDIRECT_OUTPUT = 200
CMD_GET_NEXT_STATEMENT_TARGETS = 201
CMD_SET_PROJECT_ROOTS = 202

CMD_VERSION = 501
CMD_RETURN = 502
Expand Down Expand Up @@ -395,16 +396,16 @@ def _on_run(self):

read_buffer += r
if DebugInfoHolder.DEBUG_RECORD_SOCKET_READS:
sys.stderr.write('debugger: received >>%s<<\n' % (read_buffer,))
sys.stderr.write(u'debugger: received >>%s<<\n' % (read_buffer,))
sys.stderr.flush()

if len(read_buffer) == 0:
self.handle_except()
break
while read_buffer.find('\n') != -1:
command, read_buffer = read_buffer.split('\n', 1)
while read_buffer.find(u'\n') != -1:
command, read_buffer = read_buffer.split(u'\n', 1)

args = command.split('\t', 2)
args = command.split(u'\t', 2)
try:
cmd_id = int(args[0])
pydev_log.debug('Received command: %s %s\n' % (ID_TO_MEANING.get(str(cmd_id), '???'), command,))
Expand Down Expand Up @@ -725,7 +726,7 @@ def make_thread_suspend_str(self, thread_id, frame, stop_reason, message, suspen

abs_path_real_path_and_base = get_abs_path_real_path_and_base_from_frame(curr_frame)

myFile = pydevd_file_utils.norm_file_to_client(abs_path_real_path_and_base[0])
myFile = norm_file_to_client(abs_path_real_path_and_base[0])
if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
# myFile is a byte string encoded using the file system encoding
# convert it to utf8
Expand Down
Loading

0 comments on commit d635e22

Please sign in to comment.