The Window Independent Tracer (which will probably move elsewhere once
it is truly window-independent:-)
diff --git a/Mac/Tools/twit/TwitCore.py b/Mac/Tools/twit/TwitCore.py
new file mode 100644
index 0000000..51f6485
--- /dev/null
+++ b/Mac/Tools/twit/TwitCore.py
@@ -0,0 +1,493 @@
+# Window-interface-independent part of twit
+import sys
+import types
+import bdb
+import types
+
+SIMPLE_TYPES=(
+	types.NoneType,
+	types.IntType,
+	types.LongType,
+	types.FloatType,
+	types.ComplexType,
+	types.StringType
+)
+
+def Initialize():
+	pass
+
+class DebuggerStuff(bdb.Bdb):
+
+	def __init__(self, parent):
+		bdb.Bdb.__init__(self)
+		self.parent = parent
+		self.exception_info = (None, None)
+		self.reason = 'Not running'
+		self.reset()
+		
+	def reset(self):
+		bdb.Bdb.reset(self)
+		self.forget()
+	
+	def forget(self):
+		self.lineno = None
+		self.stack = []
+		self.curindex = 0
+		self.curframe = None
+	
+	def setup(self, f, t):
+		self.forget()
+		self.stack, self.curindex = self.get_stack(f, t)
+		self.curframe = self.stack[self.curindex][0]
+		
+	def interaction(self, frame, traceback):
+		self.setup(frame, traceback)
+		self.parent.interact()
+		self.exception_info = (None, None)
+
+	def user_call(self, frame, argument_list):
+		self.reason = 'Calling function'
+		self.interaction(frame, None)
+			
+	def user_line(self, frame):
+		self.reason = 'Stopped'
+		self.interaction(frame, None)
+		
+	def user_return(self, frame, return_value):
+		self.reason = 'Returning from function'
+		self.interaction(frame, None)
+				
+	def user_exception(self, frame, (exc_type, exc_value, exc_traceback)):
+		self.reason = 'Exception occurred'
+		self.exception_info = (exc_type, exc_value)
+		self.interaction(frame, exc_traceback)
+
+	def getexception(self):
+		tp, value = self.exception_info
+		if tp <> None and type(tp) <> type(''):
+			tp = tp.__name__
+		if value <> None and type(value) <> type(''):
+			value = `value`
+		return tp, value
+		
+	def getstacktrace(self):
+		names, locations = [], []
+		for frame, lineno in self.stack:
+			name = frame.f_code.co_name
+			if not name:
+				name = "<lambda>"
+			elif name == '?': 
+				name = "<not a function>"
+			else:
+				name = name + '()'
+			names.append(name)
+			
+			if lineno == -1:
+				lineno = getframelineno(frame)
+				
+			modname = getframemodname(frame)
+			if not modname: modname = "<unknown>"	
+
+			locations.append("%s:%d" % (modname, lineno))
+		return names, locations
+		
+	def getframe(self, number):
+		if number < 0 or number >= len(self.stack):
+			return None
+		return self.stack[number][0]
+
+	def getframevars(self, number, show_complex=1, show_system=1):
+		frame = self.getframe(number)
+		if not frame:
+			return [], []
+		return getvarsfromdict(frame.f_locals, show_complex, show_system)
+		
+	def getframevar(self, number, var):
+		frame = self.getframe(number)
+		return frame.f_locals[var]
+
+	def getframefilepos(self, frameno):
+		if frameno == None or frameno < 0 or frameno >= len(self.stack):
+			return None, None, None
+		frame, line = self.stack[frameno]
+		if line == -1:
+			line = getframelineno(frame)
+		modname = getframemodname(frame)
+		filename = frame.f_code.co_filename
+		return filename, modname, line
+
+	def getprogramstate(self):
+		return self.reason
+	
+class Application:
+	"""Base code for the application"""
+	
+	def mi_init(self, run_args, pm_args):
+		self.dbg = DebuggerStuff(self)
+		self.run_dialog = self.new_stack_browser(self)
+		self.run_dialog.open()
+		self.module_dialog = None
+		self.initial_cmd = None
+		if pm_args:
+			while pm_args.tb_next <> None:
+				pm_args = pm_args.tb_next
+			self.dbg.setup(pm_args.tb_frame, pm_args)
+			self.run_dialog.setsession_pm()
+		elif run_args:
+			self.run_dialog.setsession_run()
+			self.initial_cmd = run_args
+		else:
+			self.run_dialog.setsession_none()
+		
+	def breaks_changed(self, filename):
+		self.run_dialog.breaks_changed(filename)
+		if self.module_dialog:
+			self.module_dialog.breaks_changed(filename)
+	
+	def to_debugger(self):
+		apply(self.dbg.run, self.initial_cmd)
+		
+	def interact(self):
+		# Interact with user. First, display correct info
+		self.run_dialog.update_views()
+		if self.module_dialog:
+			self.module_dialog.update_views()
+		
+		# Next, go into mainloop
+		self.one_mainloop()
+		
+		# Finally (before we start the debuggee again) show state
+		self.run_dialog.show_it_running()
+		
+	def quit_bdb(self):
+		self.dbg.set_quit()
+		
+	def run(self):
+		cmd = AskString('Statement to execute:')
+		self.initial_cmd = (cmd, None, None)
+		self.run_dialog.setsession_run()
+		self.exit_mainloop()
+
+	def cont(self):
+		self.dbg.set_continue()
+		self.exit_mainloop()
+				
+	def step(self, frame):
+		self.dbg.set_next(frame)
+		self.exit_mainloop()
+		
+	def step_in(self):
+		self.dbg.set_step()
+		self.exit_mainloop()
+		
+	def step_out(self, frame):
+		self.dbg.set_return(frame)
+		self.exit_mainloop()
+		
+	def quit(self):
+		self.do_quit()
+		
+	def browse(self, module):
+		if not self.module_dialog:
+			self.module_dialog = self.new_module_browser(self)
+			self.module_dialog.open(module)
+		else:
+			self.module_dialog.focus(module)
+	
+	def browse_var(self, var):
+		b = self.new_var_browser(self, var)
+		
+class StackBrowser:
+	"""Base code for stack browser"""
+	def mi_open(self):
+		"""Setup initial data structures"""
+		self.create_items()
+		self.cur_stackitem = None
+		self.cur_source = None
+		self.cur_modname = None
+		self.cur_line = None
+		self.show_complex = 1
+		self.show_system = 0
+		self.setup()
+
+	# create_items(self) should create self.modules, self.vars and self.source
+	
+	def setup(self):
+		SetWatch()
+		"""Fill the various widgets with values"""
+		name, value = self.parent.dbg.getexception()
+		self.setexception(name, value)
+		self.setprogramstate(self.parent.dbg.getprogramstate())
+		
+		names, locations = self.parent.dbg.getstacktrace()
+		self.stack.setcontent(names, locations)
+		self.cur_stackitem = len(names)-1
+		self.stack.select(self.cur_stackitem)
+		self.setup_frame()
+		
+	def setup_frame(self):
+		"""Setup frame-dependent widget data"""
+		SetWatch()
+		self.cont_varnames, self.cont_varvalues = \
+			self.parent.dbg.getframevars(self.cur_stackitem, 
+			self.show_complex, self.show_system)
+		self.vars.setcontent(self.cont_varnames, self.cont_varvalues)
+		self.set_var_buttons()
+	
+		msg = ""
+		if self.cur_stackitem == None:
+			self.cur_source = None
+			self.cur_modname = None
+			self.cur_line = None
+			msg = "No stackframe selected"
+		else:
+			self.cur_source, self.cur_modname, optnextline = \
+				self.parent.dbg.getframefilepos(self.cur_stackitem)
+			if optnextline >= 0:
+				self.cur_line = optnextline
+			if self.cur_source == '<string>':
+				self.cur_source = None
+				msg = "Executing from <string>"
+			print 'SOURCE', self.cur_source
+			print 'LINE', self.cur_line
+				
+		self.setsource(msg)
+		self.source.setcurline(self.cur_line)
+		self.breaks_changed(self.cur_source)
+		
+		
+		SetCursor()
+		
+	# setsource(msg) should display cur_source+content, or msg if None
+	
+	def show_it_running(self):
+		self.setprogramstate("Running")
+
+	def update_views(self):
+		self.setup()
+
+	def click_stack(self, number, *dummy):
+		if number == self.cur_stackitem: return
+		self.cur_stackitem = number
+		self.stack.select(self.cur_stackitem)
+		self.setup_frame()
+				
+	def click_var(self, var, *dummy):
+		v = self.parent.dbg.getframevar(self.cur_stackitem, var)
+		self.parent.browse_var(v)
+		
+	def click_source(self, lineno, inborder):
+		if not inborder:
+			self.source.select(lineno)
+			self.cur_line = lineno
+		if lineno == None or not self.cur_source or not inborder:
+			return
+		if self.parent.dbg.get_break(self.cur_source, lineno):
+			self.parent.dbg.clear_break(self.cur_source, lineno)
+		else:
+			self.parent.dbg.set_break(self.cur_source, lineno)
+		self.parent.breaks_changed(self.cur_source)
+		
+	def breaks_changed(self, filename):
+		if filename == self.cur_source:
+			list = self.parent.dbg.get_file_breaks(filename)
+			self.source.setbreaks(list)
+		
+	def click_quit(self):
+		self.parent.quit()
+		
+	def click_run(self):
+		self.parent.run()
+		
+	def click_continue(self):
+		self.parent.cont()
+		
+	def click_step(self):
+		if self.cur_stackitem <> None:
+			frame = self.parent.dbg.getframe(self.cur_stackitem)
+			self.parent.step(frame)
+		else:
+			self.parent.step_in()
+		
+	def click_step_in(self):
+		self.parent.step_in()
+		
+	def click_step_out(self):
+		if self.cur_stackitem <> None:
+			frame = self.parent.dbg.getframe(self.cur_stackitem)
+			self.parent.step_out(frame)
+		else:
+			self.parent.step_in()
+		
+	def click_browse(self):
+		self.parent.browse(self.cur_modname)
+		
+	def click_edit(self):
+		lino = self.cur_line
+		if not lino:
+			lino = 1
+		if self.cur_source:
+			self.parent.edit(self.cur_source, lino)
+
+class ModuleBrowser:
+	"""Base code for a module-browser"""
+
+	def mi_open(self, module):
+		"""Setup initial data structures"""
+		self.create_items()
+		self.cur_module = module
+		self.cur_source = None
+		self.cur_line = None
+		self.cont_modules = []
+		self.value_windows = []
+		self.setup()
+
+	# create_items(self) should create self.modules, self.vars and self.source
+	
+	def setup(self):
+		"""Fill the various widgets with values"""
+		SetWatch()
+		modnames = getmodulenames()
+		if not self.cur_module in modnames:
+			self.cur_module = None
+		if modnames <> self.cont_modules:
+			self.modules.setcontent(modnames)
+			self.cont_modules = modnames
+		if self.cur_module:
+			self.modules.select(self.cont_modules.index(self.cur_module))
+		else:
+			self.modules.select(None)
+		self.setup_module()
+		
+	def setup_module(self):
+		"""Setup module-dependent widget data"""
+		SetWatch()
+		if not self.cur_module:
+			self.vars.setcontent([], [])
+		else:
+			self.cont_varnames, self.cont_varvalues = getmodulevars(self.cur_module)
+			self.vars.setcontent(self.cont_varnames, self.cont_varvalues)
+			
+		msg = ""
+		if not self.cur_module:
+			self.cur_source = None
+			msg = "No module selected"
+		else:
+			m = sys.modules[self.cur_module]
+			try:
+				self.cur_source = m.__file__
+			except AttributeError:
+				self.cur_source = None
+				msg = "Not a python module"
+		self.cur_lineno = 0	
+		self.setsource(msg)
+		self.source.select(self.cur_line)
+		self.breaks_changed(self.cur_source)
+		
+		SetCursor()
+
+	# setsource(msg) should display cur_source+content, or msg if None
+	
+	def update_views(self):
+		self.setup_module()
+	
+	def click_module(self, module, *dummy):
+		if not module or module == self.cur_module: return
+		self.focus(module)
+		
+	def focus(self, module):
+		self.cur_module = module
+		self.setup()
+		
+	def click_var(self, var, *dummy):
+		if not var: return
+		m = sys.modules[self.cur_module]
+		dict = m.__dict__
+		self.parent.browse_var(dict[var])
+				
+	def click_source(self, lineno, inborder):
+		if not inborder:
+			self.source.select(lineno)
+			self.cur_lineno = lineno
+		if lineno == None or not self.cur_source or not inborder:
+			return
+		if self.parent.dbg.get_break(self.cur_source, lineno):
+			self.parent.dbg.clear_break(self.cur_source, lineno)
+		else:
+			self.parent.dbg.set_break(self.cur_source, lineno)
+		self.parent.breaks_changed(self.cur_source)
+		
+	def breaks_changed(self, filename):
+		if filename == self.cur_source:
+			list = self.parent.dbg.get_file_breaks(filename)
+			self.source.setbreaks(list)
+		
+	def click_edit(self):
+		lino = self.cur_lineno
+		if not lino:
+			lino = 1
+		if self.cur_source:
+			self.parent.edit(self.cur_source, lino)
+		
+			
+def getmodulenames():
+	"""Return a list of all current modules, sorted"""
+	list = sys.modules.keys()[:]
+	list.sort()
+	return list
+	
+def getmodulevars(name):
+	"""For given module return lists with names and values"""
+	m = sys.modules[name]
+	try:
+		dict = m.__dict__
+	except AttributeError:
+		dict = {}
+	return getvarsfromdict(dict)
+	
+def getvarsfromdict(dict, show_complex=1, show_system=1):
+	allnames = dict.keys()[:]
+	allnames.sort()
+	names = []
+	for n in allnames:
+		if not show_complex:
+			if not type(dict[n]) in SIMPLE_TYPES:
+				continue
+		if not show_system:
+			if n[:2] == '__' and n[-2:] == '__':
+				continue
+		names.append(n)
+	values = []
+	for n in names:
+		v = pretty(dict[n])
+		values.append(v)
+	return names, values
+	
+def pretty(var):
+	t = type(var)
+	if t == types.FunctionType: return '<function>'
+	if t == types.ClassType: return '<class>'
+	return `var`
+	
+def getframelineno(frame):
+	"""Given a frame return the line number"""
+	return getcodelineno(frame.f_code)
+	
+def getfunclineno(func):
+	"""Given a function return the line number"""
+	return getcodelineno(func.func_code)
+	
+def getcodelineno(cobj):
+	"""Given a code object return the line number"""
+	code = cobj.co_code
+	lineno = -1
+	if ord(code[0]) == 127: # SET_LINENO instruction
+		lineno = ord(code[1]) | (ord(code[2]) << 8)
+	return lineno
+
+def getframemodname(frame):
+	"""Given a frame return the module name"""
+	globals = frame.f_globals
+	if globals.has_key('__name__'):
+		return globals['__name__']
+	return None