DebugClients/Python/BreakpointWatch.py

branch
debugger speed
changeset 5178
878ce843ca9f
parent 5174
8c48f5e0cd92
child 5389
9b1c800daff3
equal deleted inserted replaced
5174:8c48f5e0cd92 5178:878ce843ca9f
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the breakpoint and watch class.
8 """
9
10 import os
11
12
13 class Breakpoint:
14 """
15 Breakpoint class.
16
17 Implements temporary breakpoints, ignore counts, disabling and
18 (re)-enabling, and conditionals.
19
20 Breakpoints are indexed by the file,line tuple using breaks. It
21 points to a single Breakpoint instance. This is rather different to
22 the original bdb, since there may be more than one breakpoint per line.
23
24 To test for a specific line in a file there is another dict breakInFile,
25 which is indexed only by filename and holds all line numbers where
26 breakpoints are.
27 """
28 breaks = {} # indexed by (filename, lineno) tuple: Breakpoint
29 breakInFile = {} # indexed by filename: [lineno]
30 breakInFrameCache = {}
31
32 def __init__(self, filename, lineno, temporary=False, cond=None):
33 """
34 Constructor
35
36 @param filename file name where a breakpoint is set
37 @type str
38 @param lineno line number of the breakpoint
39 @type int
40 @keyparam temporary flag to indicate a temporary breakpoint
41 @type bool
42 @keyparam cond Python expression which dynamically enables this bp
43 @type str
44 """
45 filename = os.path.abspath(filename)
46 self.file = filename
47 self.line = lineno
48 self.temporary = temporary
49 self.cond = cond
50 self.enabled = True
51 self.ignore = 0
52 self.hits = 0
53 self.breaks[filename, lineno] = self
54 lines = self.breakInFile.setdefault(filename, [])
55 if lineno not in lines:
56 lines.append(lineno)
57 self.breakInFrameCache.clear()
58
59 def deleteMe(self):
60 """
61 Public method to clear this breakpoint.
62 """
63 try:
64 del self.breaks[(self.file, self.line)]
65 self.breakInFile[self.file].remove(self.line)
66 if not self.breakInFile[self.file]:
67 del self.breakInFile[self.file]
68 except KeyError:
69 pass
70
71 def enable(self):
72 """
73 Public method to enable this breakpoint.
74 """
75 self.enabled = True
76
77 def disable(self):
78 """
79 Public method to disable this breakpoint.
80 """
81 self.enabled = False
82
83 @staticmethod
84 def clear_break(filename, lineno):
85 """
86 Public method reimplemented from bdb.py to clear a breakpoint.
87
88 @param filename file name of the bp to retrieve
89 @type str
90 @param lineno line number of the bp to retrieve
91 @type int
92 """
93 bp = Breakpoint.breaks.get((filename, lineno))
94 if bp:
95 bp.deleteMe()
96 Breakpoint.breakInFrameCache.clear()
97
98 @staticmethod
99 def clear_all_breaks():
100 """
101 Public method to clear all breakpoints.
102 """
103 for bp in Breakpoint.breaks.copy():
104 bp.deleteMe()
105 Breakpoint.breakInFrameCache.clear()
106
107 @staticmethod
108 def get_break(filename, lineno):
109 """
110 Public method to get the breakpoint of a particular line.
111
112 Because eric6 supports only one breakpoint per line, this
113 method will return only one breakpoint.
114
115 @param filename file name of the bp to retrieve
116 @type str
117 @param lineno line number of the bp to retrieve
118 @type int
119 @return Breakpoint or None, if there is no bp
120 @rtype Breakpoint object or None
121 """
122 return Breakpoint.breaks.get((filename, lineno))
123
124 @staticmethod
125 def effectiveBreak(filename, lineno, frame):
126 """
127 Public method to determine which breakpoint for this filename:lineno
128 is to be acted upon.
129
130 Called only if we know there is a bpt at this
131 location. Returns breakpoint that was triggered and a flag
132 that indicates if it is ok to delete a temporary bp.
133
134 @param filename file name of the bp to retrieve
135 @type str
136 @param lineno line number of the bp to retrieve
137 @type int
138 @param frame the current execution frame
139 @type frame object
140 @return tuple of Breakpoint and a flag to indicate, that a
141 temporary breakpoint may be deleted
142 @rtype tuple of Breakpoint, bool
143 """
144 b = Breakpoint.breaks[filename, lineno]
145 if not b.enabled:
146 return (None, False)
147
148 # Count every hit when bp is enabled
149 b.hits += 1
150 if not b.cond:
151 # If unconditional, and ignoring,
152 # go on to next, else break
153 if b.ignore > 0:
154 b.ignore -= 1
155 return (None, False)
156 else:
157 # breakpoint and marker that's ok
158 # to delete if temporary
159 return (b, True)
160 else:
161 # Conditional bp.
162 # Ignore count applies only to those bpt hits where the
163 # condition evaluates to true.
164 try:
165 val = eval(b.cond, frame.f_globals, frame.f_locals)
166 if val:
167 if b.ignore > 0:
168 b.ignore -= 1
169 # continue
170 else:
171 return (b, True)
172 # else:
173 # continue
174 except Exception:
175 # if eval fails, most conservative
176 # thing is to stop on breakpoint
177 # regardless of ignore count.
178 # Don't delete temporary,
179 # as another hint to user.
180 return (b, False)
181 return (None, False)
182
183
184 class Watch:
185 """
186 Watch class.
187
188 Implements temporary watches, ignore counts, disabling and
189 (re)-enabling, and conditionals.
190 """
191 watches = []
192
193 def __init__(self, cond, compiledCond, flag, temporary=False):
194 """
195 Constructor
196
197 @param cond condition as string with flag
198 @type str
199 @param compiledCond precompiled condition
200 @type code object
201 @param flag indicates type of watch (created or changed)
202 @type str
203 @keyparam temporary flag for temporary watches
204 @type bool
205 """
206 # Should not occur
207 if not cond:
208 return
209
210 self.cond = cond
211 self.compiledCond = compiledCond
212 self.temporary = temporary
213
214 self.enabled = True
215 self.ignore = 0
216
217 self.created = False
218 self.changed = False
219 if flag == '??created??':
220 self.created = True
221 elif flag == '??changed??':
222 self.changed = True
223
224 self.values = {}
225 self.watches.append(self)
226
227 def deleteMe(self):
228 """
229 Public method to clear this watch expression.
230 """
231 try:
232 del self.watches[self]
233 except ValueError:
234 pass
235
236 def enable(self):
237 """
238 Public method to enable this watch.
239 """
240 self.enabled = True
241
242 def disable(self):
243 """
244 Public method to disable this watch.
245 """
246 self.enabled = False
247
248 @staticmethod
249 def clear_watch(cond):
250 """
251 Public method to clear a watch expression.
252
253 @param cond expression of the watch expression to be cleared
254 @type str
255 """
256 try:
257 Watch.watches.remove(Watch.get_watch(cond))
258 except ValueError:
259 pass
260
261 @staticmethod
262 def clear_all_watches():
263 """
264 Public method to clear all watch expressions.
265 """
266 del Watch.watches[:]
267
268 @staticmethod
269 def get_watch(cond):
270 """
271 Public method to get a watch expression.
272
273 @param cond expression of the watch expression to be cleared
274 @type str
275 @return reference to the watch point
276 @rtype Watch or None
277 """
278 for b in Watch.watches:
279 if b.cond == cond:
280 return b
281
282 @staticmethod
283 def effectiveWatch(frame):
284 """
285 Public method to determine, if a watch expression is effective.
286
287 @param frame the current execution frame
288 @type frame object
289 @return tuple of watch expression and a flag to indicate, that a
290 temporary watch expression may be deleted
291 @rtype tuple of Watch, int
292 """
293 for b in Watch.watches:
294 if not b.enabled:
295 continue
296 try:
297 val = eval(b.compiledCond, frame.f_globals, frame.f_locals)
298 if b.created:
299 if frame in b.values:
300 continue
301 else:
302 b.values[frame] = [1, val, b.ignore]
303 return (b, True)
304
305 elif b.changed:
306 try:
307 if b.values[frame][1] != val:
308 b.values[frame][1] = val
309 else:
310 continue
311 except KeyError:
312 b.values[frame] = [1, val, b.ignore]
313
314 if b.values[frame][2] > 0:
315 b.values[frame][2] -= 1
316 continue
317 else:
318 return (b, True)
319
320 elif val:
321 if b.ignore > 0:
322 b.ignore -= 1
323 continue
324 else:
325 return (b, True)
326 except Exception:
327 continue
328 return (None, False)
329
330
331 #
332 # eflag: noqa = M702

eric ide

mercurial