Python & EPIPE: Handling Chg Proposal
Randall Hopper
aa8vb at yahoo.com
Tue Feb 1 14:31:38 EST 2000
The gist of the problem is that any program which writes to stdout or
stderr really needs to handle EPIPE to avoid tripping python error
tracebacks, and doing so is non-trivial and obfuscates otherwise-simple
Python code.
Basically, this is buggy Python code:
python -c "for i in xrange(100000): print 'Lots of output'" | head
The attached script demonstrates EPIPE handling in detail. Run as:
broken_pipe.py | head
==============================================================================
PROPOSAL:
Add an "epipe_error" attribute to the file object. It would determine
whether EPIPE errors from read()/write() calls generate SystemExit
or IOError exceptions.
==============================================================================
By default EPIPEs received from the stdin/out/err files would generate
SystemExit exceptions (epipe_error = 0). All other files would default to
IOError exceptions (epipe_error = 1).
Comments?
--
Randall Hopper
aa8vb at yahoo.com
-------------- next part --------------
#!/usr/bin/env python
"""
------------------------------------------------------------------------------
broken_pipe.py - Demonstrate how EPIPE handling really convolutes
Python code. (R. Hopper, 2/2000)
The jist of it is this:
python -c "for i in xrange(100000): print 'Lots of output'" | more
This is buggy Python code.
Why? Because any normal write() connected to stdout can generate
EPIPE, which Python turns into an IOError exception, which
materializes as a Python exception dump and error message on
termination.
Substitute 'more' with your favorite pager ('head', etc.). Quit
the pager after the first screenful appears to make the program fail.
What about using xrange(10000), or xrange(100)? Depends on the OS
buffer sizes.
So with the current EPIPE handling in Python , the only correct
solution is for every program that writes to stdout (or stderr) to
handle IOError exceptions for EPIPE explicitly.
However, special-handling EPIPE IOErrors specifically really convolutes
otherwise simple Python code:
------------------------------------------------------------------------------
"""
import sys, string, errno
def write_a_rec():
print '<A record from the database>'
def write_recs():
for i in xrange(100000):
#--------------------------------------------------------------------------
# In write_a_rec(), we could do some things that may generate different
# exceptions. Among them, IOErrors, and among those EPIPE errors
# specifically. Special-handling EPIPE is a real pain.
#--------------------------------------------------------------------------
# So we can't just call this method. Unprotected stdout/err write()s
# to stdout and stderr can produce errors.
## write_a_rec()
# We have to special-handle EPIPE IOErrors from write()s to stdout/err, but
# we want to preserve all the other exceptions.
#
# To avoid duplicating exception cleanup code, we use a nested try.
# This would be unnecessary if we could snag just EPIPE IOErrors with
# an except clause.
try:
try:
write_a_rec()
except IOError, x:
if x.errno == errno.EPIPE:
sys.stderr.write( "\nHandling EPIPE...exiting quietly.\n"
" (Normally, nothing would be printed.)"
"\n\n" )
sys.exit(1)
else:
raise
except:
# Do <NEEDED CLEANUP BLOCK A>
raise
#
# MAIN
#
write_recs()
"""
------------------------------------------------------------------------------
Possible Solutions:
1) Have Python not print a traceback and error if EPIPE IOErrors
propagate up to the top stack level (what if this is a socket though;
then you want the error). Bad idea.
2) Throw a separate exception for EPIPE (IOErrorEPIPE?) so you don't have
to do double-try's to process them. (No, breaks existing code, and
still demands EPIPE handling for all programs that write more than
a few lines to stdout/stderr.
=> 3) Have a file descriptor attribute (such as "epipe_error") that
selectively sets whether receiving EPIPE is turned into an IOError
or a SystemExit if it propagates up to the top stack level.
In other words, chose per file descriptor, whether EPIPE should
be a hard error or not. stdout/stderr's default could be "no",
while for all other files, the default would be "yes".
Alternatively, the flag could designate whether SystemExit or
IOError is thrown to start with.
------------------------------------------------------------------------------
"""
More information about the Python-list
mailing list