Improved some error messages for command line processing.
[python/dscho.git] / Mac / Tools / twit / TwitCore.py
blob8cd8c0e428a5d360fa5aab6794833390c7abc5f7
1 # Window-interface-independent part of twit
2 import sys
3 import types
4 import bdb
5 import types
6 import os
8 SIMPLE_TYPES=(
9 types.NoneType,
10 types.IntType,
11 types.LongType,
12 types.FloatType,
13 types.ComplexType,
14 types.StringType
17 # XXXX Mac-specific
18 ICON_NORMAL=500
19 ICON_RETURN=503
20 ICON_CALL=504
21 ICON_ZERO=505
22 ICON_DEAD=506
24 class DebuggerStuff(bdb.Bdb):
26 def __init__(self, parent):
27 bdb.Bdb.__init__(self)
28 self.parent = parent
29 self.exception_info = (None, None)
30 self.reason = 'Not running'
31 self.icon = ICON_NORMAL
32 self.reset()
34 def reset(self):
35 bdb.Bdb.reset(self)
36 self.forget()
38 def forget(self):
39 self.lineno = None
40 self.stack = []
41 self.curindex = 0
42 self.curframe = None
44 def run(self, cmd, locals, globals):
45 self.reason = 'Running'
46 bdb.Bdb.run(self, cmd, locals, globals)
47 print 'RETURN from run'
48 self.reason = 'Not running'
50 def setup(self, f, t):
51 self.forget()
52 self.stack, self.curindex = self.get_stack(f, t)
53 self.curframe = self.stack[self.curindex][0]
55 def interaction(self, frame, traceback):
56 self.setup(frame, traceback)
57 self.parent.interact()
58 self.exception_info = (None, None)
60 # def user_call(self, frame, argument_list):
61 # self.reason = 'Calling'
62 # self.icon = ICON_CALL
63 # self.interaction(frame, None)
65 def user_line(self, frame):
66 self.reason = 'Stopped'
67 self.icon = ICON_NORMAL
68 self.interaction(frame, None)
70 def user_return(self, frame, return_value):
71 self.reason = 'Returning'
72 self.icon = ICON_RETURN
73 self.interaction(frame, None)
75 def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
76 self.reason = 'Exception occurred'
77 self.icon = ICON_DEAD
78 self.parent.setstate('tb')
79 self.exception_info = (exc_type, exc_value)
80 self.interaction(frame, exc_traceback)
82 def getexception(self):
83 tp, value = self.exception_info
84 if tp <> None and type(tp) <> type(''):
85 tp = tp.__name__
86 if value <> None and type(value) <> type(''):
87 value = `value`
88 return tp, value
90 def getstacktrace(self):
91 names, locations = [], []
92 for frame, lineno in self.stack:
93 name = frame.f_code.co_name
94 if not name:
95 name = "<lambda>"
96 elif name == '?':
97 name = "<not a function>"
98 else:
99 name = name + '()'
100 names.append(name)
102 if lineno == -1:
103 lineno = getframelineno(frame)
105 modname = getframemodname(frame)
106 if not modname: modname = "<unknown>"
108 locations.append("%s:%d" % (modname, lineno))
109 return names, locations
111 def getframe(self, number):
112 if number < 0 or number >= len(self.stack):
113 return None
114 return self.stack[number][0]
116 def getframevars(self, number, show_complex=1, show_system=1):
117 frame = self.getframe(number)
118 if not frame:
119 return [], []
120 return getvarsfromdict(frame.f_locals, show_complex, show_system)
122 def getframevar(self, number, var):
123 frame = self.getframe(number)
124 return frame.f_locals[var]
126 def getframefilepos(self, frameno):
127 if frameno == None or frameno < 0 or frameno >= len(self.stack):
128 return None, None, None
129 frame, line = self.stack[frameno]
130 if line == -1:
131 line = getframelineno(frame)
132 modname = getframemodname(frame)
133 filename = frame.f_code.co_filename
134 return filename, modname, line
136 def getprogramstate(self):
137 return self.reason
139 class Application:
140 """Base code for the application"""
142 def mi_init(self, sessiontype, arg):
143 self.dbg = DebuggerStuff(self)
144 self.run_dialog = self.new_stack_browser(self)
145 self.run_dialog.open()
146 self.module_dialog = None
147 self.initial_cmd = None
148 self.cur_string_name = None
149 if sessiontype == 'tb':
150 while arg.tb_next <> None:
151 arg = arg.tb_next
152 self.dbg.setup(arg.tb_frame, arg)
153 self.run_dialog.setup()
154 elif sessiontype == 'run':
155 self.initial_cmd = arg
157 def breaks_changed(self, filename):
158 self.run_dialog.breaks_changed(filename)
159 if self.module_dialog:
160 self.module_dialog.breaks_changed(filename)
162 def to_debugger(self):
163 cmd = self.initial_cmd
164 self.initial_cmd = None
165 self.setstate('run')
166 self.switch_to_app()
167 apply(self.dbg.run, cmd)
168 self.setstate('none')
169 self.switch_to_dbg()
170 self.run_dialog.update_views()
171 if self.module_dialog:
172 self.module_dialog.update_views()
174 def interact(self):
175 # Interact with user. First, display correct info
176 self.switch_to_dbg()
177 self.run_dialog.update_views()
178 if self.module_dialog:
179 self.module_dialog.update_views()
181 # Next, go into mainloop
182 self.one_mainloop()
184 # Finally (before we start the debuggee again) show state
185 self.switch_to_app()
186 self.run_dialog.show_it_running()
188 def quit_bdb(self):
189 self.dbg.set_quit()
191 def run(self):
192 cmd = self.AskString('Statement to execute:')
193 self.runstring(cmd)
195 def runfile(self, path):
196 dir, file = os.path.split(path)
197 try:
198 os.chdir(dir)
199 except os.error, arg:
200 self.Message("%s: %s"%(dir, arg))
201 return
202 ns = {'__name__':'__main__', '__file__':path}
203 cmd = "execfile('%s')"%file
204 self.runstring(cmd, ns, ns)
206 def runstring(self, cmd, globals={}, locals={}):
207 self.cur_string_name = '<string: "%s">'%cmd
208 try:
209 cmd = compile(cmd, self.cur_string_name, 'exec')
210 except SyntaxError, arg:
211 self.Message('Syntax error: %s'%`arg`)
212 return
213 self.initial_cmd = (cmd, globals, locals)
214 self.exit_mainloop()
216 def cont(self):
217 self.dbg.set_continue()
218 self.exit_mainloop()
220 def step(self, frame):
221 self.dbg.set_next(frame)
222 self.exit_mainloop()
224 def step_in(self):
225 self.dbg.set_step()
226 self.exit_mainloop()
228 def step_out(self, frame):
229 self.dbg.set_return(frame)
230 self.exit_mainloop()
232 def kill(self):
233 self.dbg.set_quit()
234 self.exit_mainloop()
236 def quit(self):
237 self.do_quit()
239 def browse(self, module):
240 if not self.module_dialog:
241 self.module_dialog = self.new_module_browser(self)
242 self.module_dialog.open(module)
243 else:
244 self.module_dialog.focus(module)
246 def browse_var(self, var):
247 b = self.new_var_browser(self, var)
249 class StackBrowser:
250 """Base code for stack browser"""
251 def mi_open(self):
252 """Setup initial data structures"""
253 self.cur_stackitem = None
254 self.cur_source = None
255 self.cur_modname = None
256 self.cur_line = None
257 self.show_complex = 1
258 self.show_system = 0
259 self.setup()
261 # create_items(self) should create self.modules, self.vars and self.source
263 def setup(self):
264 self.parent.SetWatch()
265 """Fill the various widgets with values"""
266 name, value = self.parent.dbg.getexception()
267 self.setexception(name, value)
268 self.setprogramstate(self.parent.dbg.getprogramstate())
270 names, locations = self.parent.dbg.getstacktrace()
271 self.stack_setcontent(names, locations)
272 self.cur_stackitem = len(names)-1
273 self.stack_select(self.cur_stackitem)
274 self.setup_frame()
276 def setup_frame(self):
277 """Setup frame-dependent widget data"""
278 self.parent.SetWatch()
279 self.cont_varnames, self.cont_varvalues = \
280 self.parent.dbg.getframevars(self.cur_stackitem,
281 self.show_complex, self.show_system)
282 self.setvars()
283 self.set_var_buttons()
285 msg = ""
286 if self.cur_stackitem == None:
287 self.cur_source = None
288 self.cur_modname = None
289 self.cur_line = None
290 msg = "No stackframe selected"
291 else:
292 self.cur_source, self.cur_modname, optnextline = \
293 self.parent.dbg.getframefilepos(self.cur_stackitem)
294 if optnextline >= 0:
295 self.cur_line = optnextline
296 if self.cur_source == '<string>':
297 self.cur_source = None
298 msg = "Executing from unknown <string>"
299 elif type(self.cur_source) == types.StringType and \
300 self.cur_source[:8] == '<string:':
301 msg = "Executing from "+self.cur_source
302 self.cur_source = None
304 self.setsource(msg)
305 if not self.cur_line:
306 self.source_setline(1, ICON_ZERO)
307 else:
308 self.source_setline(self.cur_line, self.parent.dbg.icon)
309 self.breaks_changed(self.cur_source)
312 self.parent.SetCursor()
314 # setsource(msg) should display cur_source+content, or msg if None
316 def show_it_running(self):
317 self.setprogramstate("Running")
319 def update_views(self):
320 self.setup()
322 def click_stack(self, number, *dummy):
323 if number == self.cur_stackitem: return
324 self.cur_stackitem = number
325 self.stack_select(self.cur_stackitem)
326 self.setup_frame()
328 def click_var(self, var, *dummy):
329 v = self.parent.dbg.getframevar(self.cur_stackitem, var)
330 self.parent.browse_var(v)
332 def click_source(self, lineno, inborder):
333 if not inborder:
334 self.source_select(lineno)
335 self.cur_line = lineno
336 if lineno == None or not self.cur_source or not inborder:
337 return
338 if self.parent.dbg.get_break(self.cur_source, lineno):
339 self.parent.dbg.clear_break(self.cur_source, lineno)
340 else:
341 self.parent.dbg.set_break(self.cur_source, lineno)
342 self.parent.breaks_changed(self.cur_source)
344 def breaks_changed(self, filename):
345 if filename == self.cur_source:
346 list = self.parent.dbg.get_file_breaks(filename)
347 self.source_setbreaks(list)
349 def click_quit(self):
350 self.parent.quit()
352 def click_run(self):
353 self.parent.run()
355 def click_continue(self):
356 self.parent.cont()
358 def click_step(self):
359 if self.cur_stackitem <> None:
360 frame = self.parent.dbg.getframe(self.cur_stackitem)
361 self.parent.step(frame)
362 else:
363 self.parent.step_in()
365 def click_step_in(self):
366 self.parent.step_in()
368 def click_step_out(self):
369 if self.cur_stackitem <> None:
370 frame = self.parent.dbg.getframe(self.cur_stackitem)
371 self.parent.step_out(frame)
372 else:
373 self.parent.step_in()
375 def click_kill(self):
376 self.parent.kill()
378 def click_browse(self):
379 self.parent.browse(self.cur_modname)
381 def click_edit(self):
382 lino = self.cur_line
383 if not lino:
384 lino = 1
385 if self.cur_source:
386 self.parent.edit(self.cur_source, lino)
388 class ModuleBrowser:
389 """Base code for a module-browser"""
391 def mi_open(self, module):
392 """Setup initial data structures"""
393 self.cur_module = module
394 self.cur_source = None
395 self.cur_line = None
396 self.cont_modules = []
397 self.value_windows = []
398 self.setup()
400 # create_items(self) should create self.modules, self.vars and self.source
402 def setup(self):
403 """Fill the various widgets with values"""
404 self.parent.SetWatch()
405 modnames = getmodulenames()
406 if not self.cur_module in modnames:
407 self.cur_module = None
408 if modnames <> self.cont_modules:
409 self.cont_modules = modnames
410 self.setmodulenames()
411 if self.cur_module:
412 self.module_select(self.cont_modules.index(self.cur_module))
413 else:
414 self.module_select(None)
415 self.setup_module()
417 def setup_module(self):
418 """Setup module-dependent widget data"""
419 self.parent.SetWatch()
420 if not self.cur_module:
421 self.cont_varnames = []
422 self.cont_varvalues = []
423 else:
424 self.cont_varnames, self.cont_varvalues = getmodulevars(self.cur_module)
425 self.setvars()
427 msg = ""
428 if not self.cur_module:
429 self.cur_source = None
430 msg = "No module selected"
431 else:
432 m = sys.modules[self.cur_module]
433 try:
434 self.cur_source = m.__file__
435 except AttributeError:
436 self.cur_source = None
437 msg = "Not a python module"
438 self.cur_lineno = 0
439 self.setsource(msg)
440 self.source_select(self.cur_line)
441 self.breaks_changed(self.cur_source)
443 self.parent.SetCursor()
445 # setsource(msg) should display cur_source+content, or msg if None
447 def update_views(self):
448 self.setup_module()
450 def click_module(self, module, *dummy):
451 if not module or module == self.cur_module: return
452 self.focus(module)
454 def focus(self, module):
455 self.cur_module = module
456 self.setup()
458 def click_var(self, var, *dummy):
459 if not var: return
460 m = sys.modules[self.cur_module]
461 dict = m.__dict__
462 self.parent.browse_var(dict[var])
464 def click_source(self, lineno, inborder):
465 if not inborder:
466 self.source_select(lineno)
467 self.cur_lineno = lineno
468 if lineno == None or not self.cur_source or not inborder:
469 return
470 if self.parent.dbg.get_break(self.cur_source, lineno):
471 self.parent.dbg.clear_break(self.cur_source, lineno)
472 else:
473 self.parent.dbg.set_break(self.cur_source, lineno)
474 self.parent.breaks_changed(self.cur_source)
476 def breaks_changed(self, filename):
477 if filename == self.cur_source:
478 list = self.parent.dbg.get_file_breaks(filename)
479 self.source_setbreaks(list)
481 def click_edit(self):
482 lino = self.cur_lineno
483 if not lino:
484 lino = 1
485 if self.cur_source:
486 self.parent.edit(self.cur_source, lino)
489 def getmodulenames():
490 """Return a list of all current modules, sorted"""
491 list = sys.modules.keys()[:]
492 list.sort()
493 return list
495 def getmodulevars(name):
496 """For given module return lists with names and values"""
497 m = sys.modules[name]
498 try:
499 dict = m.__dict__
500 except AttributeError:
501 dict = {}
502 return getvarsfromdict(dict)
504 def getvarsfromdict(dict, show_complex=1, show_system=1):
505 allnames = dict.keys()[:]
506 allnames.sort()
507 names = []
508 for n in allnames:
509 if not show_complex:
510 if not type(dict[n]) in SIMPLE_TYPES:
511 continue
512 if not show_system:
513 if n[:2] == '__' and n[-2:] == '__':
514 continue
515 names.append(n)
516 values = []
517 for n in names:
518 v = pretty(dict[n])
519 values.append(v)
520 return names, values
522 def pretty(var):
523 t = type(var)
524 if t == types.FunctionType: return '<function>'
525 if t == types.ClassType: return '<class>'
526 return `var`
528 def getframelineno(frame):
529 """Given a frame return the line number"""
530 return getcodelineno(frame.f_code)
532 def getfunclineno(func):
533 """Given a function return the line number"""
534 return getcodelineno(func.func_code)
536 def getcodelineno(cobj):
537 """Given a code object return the line number"""
538 code = cobj.co_code
539 lineno = -1
540 if ord(code[0]) == 127: # SET_LINENO instruction
541 lineno = ord(code[1]) | (ord(code[2]) << 8)
542 return lineno
544 def getframemodname(frame):
545 """Given a frame return the module name"""
546 globals = frame.f_globals
547 if globals.has_key('__name__'):
548 return globals['__name__']
549 return None