blob: 0a6644b8cf016de31d0b749c1eb6ce2526da0a9b [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001from pydevd_constants import *
2import pydevd_tracing
3import sys
4import pydev_log
5
6_original_excepthook = None
7_handle_exceptions = None
8
9
10NOTIFY_ALWAYS="NOTIFY_ALWAYS"
11NOTIFY_ON_TERMINATE="NOTIFY_ON_TERMINATE"
12
13if USE_LIB_COPY:
14 import _pydev_threading as threading
15else:
16 import threading
17
18threadingCurrentThread = threading.currentThread
19
20from pydevd_comm import GetGlobalDebugger
21
22class ExceptionBreakpoint:
23 def __init__(self, qname, notify_always, notify_on_terminate):
24 exctype = get_class(qname)
25 self.qname = qname
26 if exctype is not None:
27 self.name = exctype.__name__
28 else:
29 self.name = None
30
31 self.notify_on_terminate = int(notify_on_terminate) == 1
32 self.notify_always = int(notify_always) > 0
33 self.notify_on_first_raise_only = int(notify_always) == 2
34
35 self.type = exctype
36 self.notify = {NOTIFY_ALWAYS: self.notify_always, NOTIFY_ON_TERMINATE: self.notify_on_terminate}
37
38
39 def __str__(self):
40 return self.qname
41
42class LineBreakpoint:
43 def __init__(self, type, flag, condition, func_name, expression):
44 self.type = type
45 self.condition = condition
46 self.func_name = func_name
47 self.expression = expression
48
49 def get_break_dict(self, breakpoints, file):
50 if DictContains(breakpoints, file):
51 breakDict = breakpoints[file]
52 else:
53 breakDict = {}
54 breakpoints[file] = breakDict
55 return breakDict
56
57 def trace(self, file, line, func_name):
58 if DebugInfoHolder.DEBUG_TRACE_BREAKPOINTS > 0:
59 pydev_log.debug('Added breakpoint:%s - line:%s - func_name:%s\n' % (file, line, func_name))
60 sys.stderr.flush()
61
62 def add(self, breakpoints, file, line, func_name):
63 self.trace(file, line, func_name)
64
65 breakDict = self.get_break_dict(breakpoints, file)
66
67 breakDict[line] = self
68
69def get_exception_full_qname(exctype):
70 if not exctype:
71 return None
72 return str(exctype.__module__) + '.' + exctype.__name__
73
74def get_exception_name(exctype):
75 if not exctype:
76 return None
77 return exctype.__name__
78
79
80def get_exception_breakpoint(exctype, exceptions, notify_class):
81 name = get_exception_full_qname(exctype)
82 exc = None
83 if exceptions is not None:
84 for k, e in exceptions.items():
85 if e.notify[notify_class]:
86 if name == k:
87 return e
88 if (e.type is not None and issubclass(exctype, e.type)):
89 if exc is None or issubclass(e.type, exc.type):
90 exc = e
91 return exc
92
93#=======================================================================================================================
94# excepthook
95#=======================================================================================================================
96def excepthook(exctype, value, tb):
97 global _handle_exceptions
98 if _handle_exceptions is not None:
99 exception_breakpoint = get_exception_breakpoint(exctype, _handle_exceptions, NOTIFY_ON_TERMINATE)
100 else:
101 exception_breakpoint = None
102
103 if exception_breakpoint is None:
104 return _original_excepthook(exctype, value, tb)
105
106 #Always call the original excepthook before going on to call the debugger post mortem to show it.
107 _original_excepthook(exctype, value, tb)
108
109 if tb is None: #sometimes it can be None, e.g. with GTK
110 return
111
112 frames = []
113
114 traceback = tb
115 while tb:
116 frames.append(tb.tb_frame)
117 tb = tb.tb_next
118
119 thread = threadingCurrentThread()
120 frames_byid = dict([(id(frame),frame) for frame in frames])
121 frame = frames[-1]
122 thread.additionalInfo.exception = (exctype, value, tb)
123 thread.additionalInfo.pydev_force_stop_at_exception = (frame, frames_byid)
124 thread.additionalInfo.message = exception_breakpoint.qname
125 #sys.exc_info = lambda : (exctype, value, traceback)
126 debugger = GetGlobalDebugger()
127 debugger.force_post_mortem_stop += 1
128
129 pydevd_tracing.SetTrace(None) #no tracing from here
130 debugger.handle_post_mortem_stop(thread.additionalInfo, thread)
131
132#=======================================================================================================================
133# set_pm_excepthook
134#=======================================================================================================================
135def set_pm_excepthook(handle_exceptions_arg=None):
136 '''
137 Should be called to register the excepthook to be used.
138
139 It's only useful for uncaucht exceptions. I.e.: exceptions that go up to the excepthook.
140
141 Can receive a parameter to stop only on some exceptions.
142
143 E.g.:
144 register_excepthook((IndexError, ValueError))
145
146 or
147
148 register_excepthook(IndexError)
149
150 if passed without a parameter, will break on any exception
151
152 @param handle_exceptions: exception or tuple(exceptions)
153 The exceptions that should be handled.
154 '''
155 global _handle_exceptions
156 global _original_excepthook
157 if sys.excepthook != excepthook:
158 #Only keep the original if it's not our own excepthook (if called many times).
159 _original_excepthook = sys.excepthook
160
161 _handle_exceptions = handle_exceptions_arg
162 sys.excepthook = excepthook
163
164def restore_pm_excepthook():
165 global _original_excepthook
166 if _original_excepthook:
167 sys.excepthook = _original_excepthook
168 _original_excepthook = None
169
170
171def update_exception_hook(dbg):
172 if dbg.exception_set:
173 set_pm_excepthook(dict(dbg.exception_set))
174 else:
175 restore_pm_excepthook()
176
177def get_class( kls ):
178 if IS_PY24 and "BaseException" == kls:
179 kls = "Exception"
180 parts = kls.split('.')
181 module = ".".join(parts[:-1])
182 if module == "":
183 if IS_PY3K:
184 module = "builtins"
185 else:
186 module = "__builtin__"
187 try:
188 m = __import__( module )
189 for comp in parts[-1:]:
190 if m is None:
191 return None
192 m = getattr(m, comp, None)
193 return m
194 except ImportError:
195 return None