summaryrefslogtreecommitdiff
path: root/python/helpers/pydev/pydevd_breakpoints.py
blob: beebebf4abed7660267de6564785f1fc0ba0f0f1 (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
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
from pydevd_constants import *
import pydevd_tracing
import sys
import pydev_log

_original_excepthook = None
_handle_exceptions = None


NOTIFY_ALWAYS="NOTIFY_ALWAYS"
NOTIFY_ON_TERMINATE="NOTIFY_ON_TERMINATE"

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):
        exctype = get_class(qname)
        self.qname = qname
        if exctype is not None:
            self.name = exctype.__name__
        else:
            self.name = None

        self.notify_on_terminate = int(notify_on_terminate) == 1
        self.notify_always = int(notify_always) > 0
        self.notify_on_first_raise_only = int(notify_always) == 2

        self.type = exctype
        self.notify = {NOTIFY_ALWAYS: self.notify_always, NOTIFY_ON_TERMINATE: self.notify_on_terminate}


    def __str__(self):
        return self.qname

class LineBreakpoint:
    def __init__(self, type, flag, condition, func_name, expression):
        self.type = type
        self.condition = condition
        self.func_name = func_name
        self.expression = expression

    def get_break_dict(self, breakpoints, file):
        if DictContains(breakpoints, file):
            breakDict = breakpoints[file]
        else:
            breakDict = {}
        breakpoints[file] = breakDict
        return breakDict

    def trace(self, file, line, func_name):
        if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
            pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
            sys.stderr.flush()

    def add(self, breakpoints, file, line, func_name):
      self.trace(file, line, func_name)

      breakDict = self.get_break_dict(breakpoints, file)

      breakDict[line] = self

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, notify_class):
    name = get_exception_full_qname(exctype)
    exc = None
    if exceptions is not None:
        for k, e in exceptions.items():
          if e.notify[notify_class]:
            if name == k:
                return e
            if (e.type is not None and issubclass(exctype, e.type)):
                if exc is None or issubclass(e.type, exc.type):
                    exc = e
    return exc

#=======================================================================================================================
# excepthook
#=======================================================================================================================
def excepthook(exctype, value, tb):
    global _handle_exceptions
    if _handle_exceptions is not None:
        exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions, NOTIFY_ON_TERMINATE)
    else:
        exception_breakpoint = None

    if exception_breakpoint is None:
        return _original_excepthook(exctype, value, tb)

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

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

    frames = []

    traceback = tb
    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
    #sys.exc_info = lambda : (exctype, value, traceback)
    debugger = GetGlobalDebugger()
    debugger.force_post_mortem_stop += 1

    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_arg=None):
    '''
    Should be called to register the excepthook to be used.

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

    Can receive a parameter to stop only on some exceptions.

    E.g.:
        register_excepthook((IndexError, ValueError))

        or

        register_excepthook(IndexError)

        if passed without a parameter, will break on any exception

    @param handle_exceptions: exception or tuple(exceptions)
        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_arg
    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.exception_set:
        set_pm_excepthook(dict(dbg.exception_set))
    else:
        restore_pm_excepthook()

def get_class( kls ):
    if IS_PY24 and "BaseException" == kls:
        kls = "Exception"
    parts = kls.split('.')
    module = ".".join(parts[:-1])
    if module == "":
        if IS_PY3K:
            module = "builtins"
        else:
            module = "__builtin__"
    try:
        m = __import__( module )
        for comp in parts[-1:]:
            if m is None:
                return None
            m = getattr(m, comp, None)
        return m
    except ImportError:
        return None