NB: I have not attempted to reproduce the problem described below under Windows, or with versions of Python other than 2.7.3.
The most reliable way to elicit the problem in question is to pipe the output of the following test script through :
(under bash
):
try:
for n in range(20):
print n
except:
pass
I.e.:
% python testscript.py | :
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
My question is:
How can I modify the test script above to avoid the error message when the script is run as shown (under Unix/
bash
)?
(As the test script shows, the error cannot be trapped with a try-except
.)
The example above is, admittedly, highly artificial, but I'm running into the same problem sometimes when the output of a script of mine is piped through some 3rd party software.
The error message is certainly harmless, but it is disconcerting to end-users, so I would like to silence it.
EDIT: The following script, which differs from the original one above only in that it redefines sys.excepthook, behaves exactly like the one given above.
import sys
STDERR = sys.stderr
def excepthook(*args):
print >> STDERR, 'caught'
print >> STDERR, args
sys.excepthook = excepthook
try:
for n in range(20):
print n
except:
pass
You will need to prevent the script from writing anything to standard output. That means removing any
print
statements and any use ofsys.stdout.write
, as well as any code that calls those.The reason this is happening is that you're piping a nonzero amount of output from your Python script to something which never reads from standard input. This is not unique to the
:
command; you can get the same result by piping to any command which doesn't read standard input, such asOr for a simpler example, consider a script
printer.py
containing nothing more thanThen
will produce the same error.
When you pipe the output of one program into another, the output produced by the writing program gets backed up in a buffer, and waits for the reading program to request that data from the buffer. As long as the buffer is nonempty, any attempt to close the writing file object is supposed to fail with an error. This is the root cause of the messages you're seeing.
The specific code that triggers the error is in the C language implementation of Python, which explains why you can't catch it with a
try
/except
block: it runs after the contents of your script has finished processing. Basically, while Python is shutting itself down, it attempts to closestdout
, but that fails because there is still buffered output waiting to be read. So Python tries to report this error as it would normally, butsys.excepthook
has already been removed as part of the finalization procedure, so that fails. Python then tries to print a message tosys.stderr
, but that has already been deallocated so again, it fails. The reason you see the messages on the screen is that the Python code does contain a contingencyfprintf
to write out some output to the file pointer directly, even if Python's output object doesn't exist.Technical details
For those interested in the details of this procedure, let's take a look at the Python interpreter's shutdown sequence, which is implemented in the
Py_Finalize
function ofpythonrun.c
.PyImport_Cleanup
to finalize and deallocate all imported modules. The next-to-last task performed by this function is removing thesys
module, which mainly consists of calling_PyModule_Clear
to clear all the entries in the module's dictionary - including, in particular, the standard stream objects (the Python objects) such asstdout
andstderr
.Py_DECREF
macro. Objects whose reference count reaches zero become eligible for deallocation. Since thesys
module holds the last remaining references to the standard stream objects, when those references are unset by_PyModule_Clear
, they are then ready to be deallocated.1Deallocation of a Python file object is accomplished by the
file_dealloc
function infileobject.c
. This first invokes the Python file object'sclose
method using the aptly-namedclose_the_file
function:For a standard file object,
close_the_file(f)
delegates to the Cfclose
function, which sets an error condition if there is still data to be written to the file pointer.file_dealloc
then checks for that error condition and prints the first message you see:After printing that message, Python then attempts to display the exception using
PyErr_Print
. That delegates toPyErr_PrintEx
, and as part of its functionality,PyErr_PrintEx
attempts to access the Python exception printer fromsys.excepthook
.This would be fine if done in the normal course of a Python program, but in this situation,
sys.excepthook
has already been cleared.2 Python checks for this error condition and prints the second message as a notification.After notifying us about the missing
excepthook
, Python then falls back to printing the exception info usingPyErr_Display
, which is the default method for displaying a stack trace. The very first thing this function does is try to accesssys.stderr
.In this case, that doesn't work because
sys.stderr
has already been cleared and is inaccessible.3 So the code invokesfprintf
directly to send the third message to the C standard error stream.Interestingly, the behavior is a little different in Python 3.4+ because the finalization procedure now explicitly flushes the standard output and error streams before builtin modules are cleared. This way, if you have data waiting to be written, you get an error that explicitly signals that condition, rather than an "accidental" failure in the normal finalization procedure. Also, if you run
using Python 3.4 (after putting parentheses on the
print
statement of course), you don't get any error at all. I suppose the second invocation of Python may be consuming standard input for some reason, but that's a whole separate issue.1Actually, that's a lie. Python's import mechanism caches a copy of each imported module's dictionary, which is not released until
_PyImport_Fini
runs, later in the implementation ofPy_Finalize
, and that's when the last references to the standard stream objects disappear. Once the reference count reaches zero,Py_DECREF
deallocates the objects immediately. But all that matters for the main answer is that the references are removed from thesys
module's dictionary and then deallocated sometime later.2Again, this is because the
sys
module's dictionary is cleared completely before anything is really deallocated, thanks to the attribute caching mechanism. You can run Python with the-vv
option to see all the module's attributes being unset before you get the error message about closing the file pointer.3This particular piece of behavior is the only part that doesn't make sense unless you know about the attribute caching mechanism mentioned in previous footnotes.
I ran into this sort of issue myself today and went looking for an answer. I think a simple workaround here is to ensure you flush stdio first, so python blocks instead of failing during script shutdown. For example:
Then with this script nothing happens, as the exception (IOError: [Errno 32] Broken pipe) is suppressed by the try...except.
I realize that this is an old question, but I found it in a Google search for the error. In my case it was a coding error. One of my last statements was:
The solution was simply fixing the syntax to:
[Raspberry Pi Zero, Python 2.7.9]
In your program throws an exception that can not be caught using try/except block. To catch him, override function
sys.excepthook
:From documentation:
Illustrative example: