summaryrefslogtreecommitdiff
path: root/python/helpers/pydev/pydevd_breakpoints.py
blob: 82a230db2f478ad611566a9dc9f174b251e318f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
from pydevd_constants import *
import pydevd_tracing
import sys
import pydev_log
import pydevd_import_class

_original_excepthook = None
_handle_exceptions = None


if USE_LIB_COPY:
    import _pydev_threading as threading
else:
    import threading

threadingCurrentThread = threading.currentThread

from pydevd_comm import GetGlobalDebugger

class ExceptionBreakpoint:

    def __init__(
        self,
        qname,
        notify_always,
        notify_on_terminate,
        notify_on_first_raise_only,
        ):
        exctype = _get_class(qname)
        self.qname = qname
        if exctype is not None:
            self.name = exctype.__name__
        else:
            self.name = None

        self.notify_on_terminate = notify_on_terminate
        self.notify_always = notify_always
        self.notify_on_first_raise_only = notify_on_first_raise_only

        self.type = exctype


    def __str__(self):
        return self.qname

class LineBreakpoint:

    def __init__(self, line, condition, func_name, expression):
        self.line = line
        self.condition = condition
        self.func_name = func_name
        self.expression = expression

def get_exception_full_qname(exctype):
    if not exctype:
        return None
    return str(exctype.__module__) + '.' + exctype.__name__

def get_exception_name(exctype):
    if not exctype:
        return None
    return exctype.__name__


def get_exception_breakpoint(exctype, exceptions):
    exception_full_qname = get_exception_full_qname(exctype)

    exc = None
    if exceptions is not None:
        try:
            return exceptions[exception_full_qname]
        except KeyError:
            for exception_breakpoint in DictIterValues(exceptions):
                if exception_breakpoint.type is not None and issubclass(exctype, exception_breakpoint.type):
                    if exc is None or issubclass(exception_breakpoint.type, exc.type):
                        exc = exception_breakpoint
    return exc

#=======================================================================================================================
# _excepthook
#=======================================================================================================================
def _excepthook(exctype, value, tb):
    global _handle_exceptions
    if _handle_exceptions:
        exception_breakpoint = get_exception_breakpoint(exctype, _handle_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
        return

    frames = []

    while tb:
        frames.append(tb.tb_frame)
        tb = tb.tb_next

    thread = threadingCurrentThread()
    frames_byid = dict([(id(frame),frame) for frame in frames])
    frame = frames[-1]
    thread.additionalInfo.exception = (exctype, value, tb)
    thread.additionalInfo.pydev_force_stop_at_exception = (frame, frames_byid)
    thread.additionalInfo.message = exception_breakpoint.qname
    debugger = GetGlobalDebugger()

    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.additionalInfo, thread)

#=======================================================================================================================
# _set_pm_excepthook
#=======================================================================================================================
def _set_pm_excepthook(handle_exceptions_dict=None):
    '''
    Should be called to register the excepthook to be used.

    It's only useful for uncaught exceptions. I.e.: exceptions that go up to the excepthook.

    @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 ):
    if IS_PY24 and "BaseException" == kls:
        kls = "Exception"

    try:
        return eval(kls)
    except:
        return pydevd_import_class.ImportName(kls)