summaryrefslogtreecommitdiffstats
path: root/Lib/lib-stdwin/wdb.py
blob: 9914f3f5254cc276bd1322e1e08cfe7d316cf172 (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
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
# wdb.py -- a window-based Python debugger

# XXX To do:
# - don't fall out of bottom frame
# - is the /tmp file hack really needed?
# - also use it for post-mortem debugging


import stdwin
from stdwinevents import *
import sys
import basewin
import bdb
import repr

WIDTH = 40
HEIGHT = 8

WdbDone = 'wdb.WdbDone' # Exception to continue execution


class Wdb(bdb.Bdb, basewin.BaseWindow): # Window debugger
	
	def init(self):
		self.sourcewindows = {}
		self.framewindows = {}
		self = bdb.Bdb.init(self)
		width = WIDTH*stdwin.textwidth('0')
		height = HEIGHT*stdwin.lineheight()
		stdwin.setdefwinsize(width, height)
		self = basewin.BaseWindow.init(self, '--Stack--')
		self.closed = 0
		return self
	
	def reset(self):
		if self.closed: raise RuntimeError, 'already closed'
		bdb.Bdb.reset(self)
		self.forget()
	
	def forget(self):
		self.lineno = None
		self.stack = []
		self.curindex = 0
		self.curframe = None
		for fn in self.sourcewindows.keys():
			self.sourcewindows[fn].resetlineno()
	
	def setup(self, f, t):
		self.forget()
		self.stack, self.curindex = self.get_stack(f, t)
		self.curframe = self.stack[self.curindex][0]
		# Build a list of current frames
		cfl = []
		for f, i in self.stack: cfl.append(f)
		# Remove deactivated frame windows
		for name in self.framewindows.keys():
			fw = self.framewindows[name]
			if fw.frame not in cfl: fw.close()
			else: fw.refreshframe()
		# Refresh the stack window
		self.refreshstack()
	
	# Override Bdb methods (except user_call, for now)
	
	def user_line(self, frame):
		# This function is called when we stop or break at this line
		self.interaction(frame, None)
	
	def user_return(self, frame, return_value):
		# This function is called when a return trap is set here
		frame.f_locals['__return__'] = return_value
		self.settitle('--Return--')
		self.interaction(frame, None)
		if not self.closed:
			self.settitle('--Stack--')
	
	def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
		# This function is called if an exception occurs,
		# but only if we are to stop at or just below this level
		frame.f_locals['__exception__'] = exc_type, exc_value
		self.settitle(exc_type + ': ' + repr.repr(exc_value))
		stdwin.fleep()
		self.interaction(frame, exc_traceback)
		if not self.closed:
			self.settitle('--Stack--')
	
	# Change the title
	
	def settitle(self, title):
		self.savetitle = self.win.gettitle()
		self.win.settitle(title)
	
	# General interaction function
	
	def interaction(self, frame, traceback):
		import mainloop
		self.popup()
		self.setup(frame, traceback)
		try:
			mainloop.mainloop()
		except WdbDone:
			pass
		self.forget()
	
	# Functions whose name is do_X for some character X
	# are callable directly from the keyboard.
	
	def do_up(self):
		if self.curindex == 0:
			stdwin.fleep()
		else:
			self.curindex = self.curindex - 1
			self.curframe = self.stack[self.curindex][0]
			self.refreshstack()
	do_u = do_up
	
	def do_down(self):
		if self.curindex + 1 == len(self.stack):
			stdwin.fleep()
		else:
			self.curindex = self.curindex + 1
			self.curframe = self.stack[self.curindex][0]
			self.refreshstack()
	do_d = do_down
	
	def do_step(self):
		self.set_step()
		raise WdbDone
	do_s = do_step
	
	def do_next(self):
		self.set_next(self.curframe)
		raise WdbDone
	do_n = do_next
	
	def do_return(self):
		self.set_return(self.curframe)
		raise WdbDone
	do_r = do_return
	
	def do_continue(self):
		self.set_continue()
		raise WdbDone
	do_c = do_cont = do_continue
	
	def do_quit(self):
		self.close()
		raise WdbDone
	do_q = do_quit
	
	def do_list(self):
		fn = self.curframe.f_code.co_filename
		if not self.sourcewindows.has_key(fn):
			import wdbsrcwin
			try:
				self.sourcewindows[fn] = \
					wdbsrcwin.DebuggerSourceWindow(). \
					init(self, fn)
			except IOError:
				stdwin.fleep()
				return
		w = self.sourcewindows[fn]
		lineno = self.stack[self.curindex][1]
		w.setlineno(lineno)
		w.popup()
	do_l = do_list
	
	def do_frame(self):
		name = 'locals' + `self.curframe`[16:-1]
		if self.framewindows.has_key(name):
			self.framewindows[name].popup()
		else:
			import wdbframewin
			self.framewindows[name] = \
				wdbframewin.FrameWindow().init(self, \
					self.curframe, \
					self.curframe.f_locals, name)
	do_f = do_frame
	
	def do_globalframe(self):
		name = 'globals' + `self.curframe`[16:-1]
		if self.framewindows.has_key(name):
			self.framewindows[name].popup()
		else:
			import wdbframewin
			self.framewindows[name] = \
				wdbframewin.FrameWindow().init(self, \
					self.curframe, \
					self.curframe.f_globals, name)
	do_g = do_globalframe
	
	# Link between the debugger and the window
	
	def refreshstack(self):
		height = stdwin.lineheight() * (1 + len(self.stack))
		self.win.setdocsize((0, height))
		self.refreshall() # XXX be more subtle later
		# Also pass the information on to the source windows
		filename = self.curframe.f_code.co_filename
		lineno = self.curframe.f_lineno
		for fn in self.sourcewindows.keys():
			w = self.sourcewindows[fn]
			if fn == filename:
				w.setlineno(lineno)
			else:
				w.resetlineno()
	
	# The remaining methods override BaseWindow methods
	
	def close(self):
		if not self.closed:
			basewin.BaseWindow.close(self)
		self.closed = 1
		for key in self.sourcewindows.keys():
			self.sourcewindows[key].close()
		for key in self.framewindows.keys():
			self.framewindows[key].close()
		self.set_quit()
	
	def char(self, detail):
		try:
			func = eval('self.do_' + detail)
		except (AttributeError, SyntaxError):
			stdwin.fleep()
			return
		func()
	
	def command(self, detail):
		if detail == WC_UP:
			self.do_up()
		elif detail == WC_DOWN:
			self.do_down()
	
	def mouse_down(self, detail):
		(h, v), clicks, button, mask = detail
		i = v / stdwin.lineheight()
		if 0 <= i < len(self.stack):
			if i != self.curindex:
				self.curindex = i
				self.curframe = self.stack[self.curindex][0]
				self.refreshstack()
			elif clicks == 2:
				self.do_frame()
		else:
			stdwin.fleep()
	
	def draw(self, detail):
		import linecache, codehack, string
		d = self.win.begindrawing()
		try:
			h, v = 0, 0
			for f, lineno in self.stack:
				fn = f.f_code.co_filename
				if f is self.curframe:
					s = '> '
				else:
					s = '  '
				s = s + fn + '(' + `lineno` + ')'
				s = s + codehack.getcodename(f.f_code)
				if f.f_locals.has_key('__args__'):
					args = f.f_locals['__args__']
					if args is not None:
						s = s + repr.repr(args)
				if f.f_locals.has_key('__return__'):
					rv = f.f_locals['__return__']
					s = s + '->'
					s = s + repr.repr(rv)
				line = linecache.getline(fn, lineno)
				if line: s = s + ': ' + string.strip(line)
				d.text((h, v), s)
				v = v + d.lineheight()
		finally:
			d.close()


def run(statement):
	x = Wdb().init()
	try: x.run(statement)
	finally: x.close()

def runctx(statement, globals, locals):
	x = Wdb().init()
	try: x.runctx(statement, globals, locals)
	finally: x.close()

TESTCMD = 'import x; x.main()'

def test():
	import linecache
	linecache.checkcache()
	run(TESTCMD)