B
Bryan Olson
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()
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()