eric6/DebugClients/Python/BreakpointWatch.py

branch
maintenance
changeset 6989
8b8cadf8d7e9
parent 6942
2602857055c5
child 7249
0bf517e60f54
equal deleted inserted replaced
6938:7926553b7509 6989:8b8cadf8d7e9
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2016 - 2019 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 Breakpoint.breaks[(filename, lineno)] = self
54 lines = Breakpoint.breakInFile.setdefault(filename, [])
55 if lineno not in lines:
56 lines.append(lineno)
57 Breakpoint.breakInFrameCache.clear()
58
59 def deleteMe(self):
60 """
61 Public method to clear this breakpoint.
62 """
63 try:
64 del Breakpoint.breaks[(self.file, self.line)]
65 Breakpoint.breakInFile[self.file].remove(self.line)
66 if not Breakpoint.breakInFile[self.file]:
67 del Breakpoint.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 Static 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 Static method to clear all breakpoints.
102 """
103 Breakpoint.breaks.clear()
104 Breakpoint.breakInFile.clear()
105 Breakpoint.breakInFrameCache.clear()
106
107 @staticmethod
108 def get_break(filename, lineno):
109 """
110 Static 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 Static 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 Watch.watches.append(self)
226
227 def deleteMe(self):
228 """
229 Public method to clear this watch expression.
230 """
231 try:
232 del Watch.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 Static 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 Static method to clear all watch expressions.
265 """
266 del Watch.watches[:]
267
268 @staticmethod
269 def get_watch(cond):
270 """
271 Static 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 return None
283
284 @staticmethod
285 def effectiveWatch(frame):
286 """
287 Static method to determine, if a watch expression is effective.
288
289 @param frame the current execution frame
290 @type frame object
291 @return tuple of watch expression and a flag to indicate, that a
292 temporary watch expression may be deleted
293 @rtype tuple of Watch, int
294 """
295 for b in Watch.watches:
296 if not b.enabled:
297 continue
298 try:
299 val = eval(b.compiledCond, frame.f_globals, frame.f_locals)
300 if b.created:
301 if frame in b.values:
302 continue
303 else:
304 b.values[frame] = [1, val, b.ignore]
305 return (b, True)
306
307 elif b.changed:
308 try:
309 if b.values[frame][1] != val:
310 b.values[frame][1] = val
311 else:
312 continue
313 except KeyError:
314 b.values[frame] = [1, val, b.ignore]
315
316 if b.values[frame][2] > 0:
317 b.values[frame][2] -= 1
318 continue
319 else:
320 return (b, True)
321
322 elif val:
323 if b.ignore > 0:
324 b.ignore -= 1
325 continue
326 else:
327 return (b, True)
328 except Exception:
329 continue
330 return (None, False)
331
332
333 #
334 # eflag: noqa = M702

eric ide

mercurial