Catch stderr in non-console applications

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()
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,584
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top