DebugClients/Python3/BreakpointWatch.py

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

eric ide

mercurial