New and improved! Love Python's stack-tracing error messages, but hate the way GUI applications throw the messages away and crash silently?
Here's a module to show Python error messages that would otherwise be lost in console-less programs. Graphical applications should not require console windows, but they do need a workable destination for sys.stderr. To try it out, you can use something like: import errorwindow x = undefined_variable_name I've tried this version on Microsoft Windows 2000 and XP, and on a couple versions of Linux. I'd be happy to hear how it does on other systems. Thanks to George ([EMAIL PROTECTED]) for testing a previous version. Thanks to Robert Kern for pointing me to a bug solution. --Bryan ---------------- cut ------------------- #!/usr/bin/env python # Python module "errorwindow.py", by Bryan Olson, 2005. # This module is free software and may be used, distributed, # and modified under the same terms as Python itself. """ Importing this module redirects sys.stderr so that error output, if any, will appear in a new window. Notes on the module: It is particularly handy for graphical applications that do not otherwise have a stderr stream. It mitigates most silent failures. It uses only this file plus facilities in the Python standard distribution. There are no functions to call; just import it. Import it early; it cannot catch prior errors. It can catch syntax errors in modules imported after it, but not in '__main__'. Importing it more than once is harmless. When there is no error output, it is highly efficient, because it does nothing. Upon output to sys.stderr, it runs a new process and pipes the error output. It does not import any graphical library into your program. The new process handles that. """ import sys import os import thread if __name__ == '__main__': from threading import Thread from Tkinter import * import Queue queue = Queue.Queue(99) def read_stdin(app): while 1: data = os.read(sys.stdin.fileno(), 2048) queue.put(data) if not data: break class Application(Frame): def __init__(self, master=None): Frame.__init__(self, master) self.master.title("Error stream from %s" % sys.argv[-1]) self.pack(fill=BOTH, expand=YES) self.grid_rowconfigure(0, weight=1) self.grid_columnconfigure(0, weight=1) xscrollbar = Scrollbar(self, orient=HORIZONTAL) xscrollbar.grid(row=1, column=0, sticky=E+W) yscrollbar = Scrollbar(self) yscrollbar.grid(row=0, column=1, sticky=N+S) self.logwidget = Text(self, wrap=NONE, xscrollcommand=xscrollbar.set, yscrollcommand=yscrollbar.set) self.logwidget.grid(row=0, column=0, sticky=N+S+E+W) xscrollbar.config(command=self.logwidget.xview) yscrollbar.config(command=self.logwidget.yview) # Disallow key entry, but allow copy with <Control-c> self.logwidget.bind('<Key>', lambda x: 'break') self.logwidget.bind('<Control-c>', lambda x: None) self.after(200, self.start_thread, ()) def start_thread(self, _): t = Thread(target=read_stdin, args=(self,)) t.setDaemon(True) t.start() self.after(200, self.check_q, ()) def check_q(self, _): go = True while go: try: data = queue.get_nowait() if not data: Label(self, text="Stream closed.", relief= "sunken").grid(row=2, sticky=E+W) go = False self.logwidget.insert(END, data) self.logwidget.see(END) except Queue.Empty: self.after(200, self.check_q, ()) go = False app = Application() app.mainloop() else: class ErrorPipe(object): def __init__(self): self.lock = thread.allocate_lock() self.command = " ".join([sys.executable, __file__, sys.argv[0]]) def write(self, data): self.lock.acquire() try: if not hasattr(self, 'pipe'): self.rawpipe = os.popen(self.command, 'w') fd = os.dup(self.rawpipe.fileno()) self.pipe = os.fdopen(fd, 'w', 0) self.pipe.write(data) finally: self.lock.release() sys.stderr = ErrorPipe() -- http://mail.python.org/mailman/listinfo/python-list