Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AttributeError: StringIO instance has no attribute 'fileno'

Tags:

python

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?

like image 305
kamal Avatar asked May 05 '11 20:05

kamal


2 Answers

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()
like image 110
samplebias Avatar answered Oct 15 '22 22:10

samplebias


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

Edit:

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
like image 41
Boaz Yaniv Avatar answered Oct 15 '22 20:10

Boaz Yaniv