def captureOutput(self, func, *args, **kwargs):
pass
sys.stdout.flush()
sys.stderr.flush()
(outfd, fn) = tempfile.mkstemp()
fout = os.fdopen(outfd, 'r')
os.unlink(fn)
(errfd, fn) = tempfile.mkstemp()
ferr = os.fdopen(errfd, 'r')
os.unlink(fn)
try:
oldstdout = os.dup(sys.stdout.fileno())
oldstderr = os.dup(sys.stderr.fileno())
os.dup2(outfd, sys.stdout.fileno())
os.dup2(errfd, sys.stderr.fileno())
try:
ret = func(*args, **kwargs)
finally:
sys.stderr.flush()
sys.stdout.flush()
os.dup2(oldstdout, sys.stdout.fileno())
os.close(oldstdout)
os.dup2(oldstderr, sys.stderr.fileno())
os.close(oldstderr)
os.lseek(outfd, 0, 0)
out = fout.read()
os.lseek(errfd, 0, 0)
err = ferr.read()
finally:
fout.close()
ferr.close()
return ret, out, err
When running this code, I get an error:
AttributeError: StringIO instance has no attribute 'fileno'
Why am I getting this error and how can I correct it?
The fileno()
method is not implemented in StringIO, as it is not a real file (so has no associated file descriptor). From the source:
- fileno() is left unimplemented so that code which uses it
triggers an exception early.
It is possible that someone replaced sys.stdout
with a StringIO instance, to capture output.
For example, when I run your code this way I get the same exception:
from StringIO import StringIO
sys.stdout = StringIO()
captureOutput(testfunc)
Error:
oldstdout = os.dup(sys.stdout.fileno())
AttributeError: StringIO instance has no attribute 'fileno'
It might be best to trace your code from end to end, looking for points where sys.stdout
is being overwritten. Here's a link to another answer I gave, showing how to execute your code with tracing active:
ares% python -m trace -c -t -C ./coverage test_sio.py | grep sys.stdout
test_sio.py(47): sys.stdout = StringIO()
Are you using the standard plain python interpreter? This error may appear when you use an interpreter that overrides stdout/stderr, such as IDLE (though IDLE itself would give you a different error). It may also be caused by a library which overrides stdout/stderr.
Sometimes you can reset your stdout the the default stdout by writing sys.stdout = sys.__stdout__
, but don't count on it working always. It doesn't work in Pythonwin for instance.
Anyway, it seems that what you're trying to do with your code is to redirect stdout/stderr yourself. If that's the case, you should just go ahead and do it. I think this should work, if you have file descriptors outfd
and errfd
:
sys.stdout = os.fdopen(outfd, 'w')
sys.stderr = os.fdopen(errfd, 'w')
Now that I can see your entire code, I wouldn't use temporary files at all.
def captureOutput(self, func, *args, **kwargs):
import cStringIO # You can also use StringIO instead
sys.stderr.flush()
sys.stdout.flush()
olderr, oldout = sys.stderr, sys.stdout
try:
sys.stderr = cStringIO.StringIO()
sys.stdout = cStringIO.StringIO()
try:
ret = func(*args, **kwargs)
finally:
stderr.seek(0)
stdout.seek(0)
err = stderr.read()
out = stdout.read()
finally:
sys.stderr = olderr
sys.stdout = oldout
return ret, out, err
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With