20
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?

Jeff Mercado
  • 129,526
  • 32
  • 251
  • 272
kamal
  • 9,637
  • 30
  • 101
  • 168
  • More code is needed, what is outfd and errfd? – Joseph Lisee May 05 '11 at 20:15
  • 1
    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. – Boaz Yaniv May 05 '11 at 20:17

4 Answers4

16

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()
Community
  • 1
  • 1
samplebias
  • 37,113
  • 6
  • 107
  • 103
  • i cannot find any import of StringIO, could it be that dup() and dup2() are used ? that is causing errors? now: dup() and dup2() are explained as follows: and return FileDescriptors. os.dup(fd) Return a duplicate of file descriptor fd. Availability: Unix, Windows. os.dup2(fd, fd2) Duplicate file descriptor fd to fd2, closing the latter first if necessary. Availability: Unix, Windows. – kamal May 05 '11 at 20:48
  • @kamal I don't think `dup()` would cause that particular error. The exception occurs when an attempt is made to access the `[obj].fileno` attribute, which is missing. Are you running under an IDE? Perhaps your IDE is swapping in a StringIO, as mentioned in another answer. – samplebias May 05 '11 at 20:52
  • i am not running in an IDE, rather in a shell (bash) so i have a bash script driving these scripts using nosetests – kamal May 05 '11 at 21:02
  • @kamal Try the Python tracer - I've updated my answer with more details. – samplebias May 05 '11 at 21:12
  • this is excellent information, thanks , but i believe in this case test_sio.py is executable (has the line has no if __name__=='__main__' ) correct ? which in my case is not true – kamal May 05 '11 at 22:13
5

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
mouad
  • 67,571
  • 18
  • 114
  • 106
Boaz Yaniv
  • 6,334
  • 21
  • 30
  • Using Python 2.7.5... isn't the function cStringIO.getvalue() instead of csStringIO.read()? Also, shouldn't you close the cStringIO instances? – 2rs2ts May 30 '13 at 19:35
2

The short answer is that you ran across a bug in standard library. StringIO does not fulfill the contract of its IOBase base class. Some class wrote to the IOBase class interface, which then fails.

More specifically, Subprocess.run() or some other function used the IOBase fileno function. The subclass StringIO throws this exception because it is not a true subclass. Somewhere, one of the many users of IOBase fails. Documenting StringIO does not help this problem.

You can code around it, maybe. Or maybe not. All sorts of functions like contextlib.redirect_stdout() will fail.

Charles Merriam
  • 19,908
  • 6
  • 73
  • 83
1

My guess would be somewhere else in the code, sys.stdout or sys.stderr was reassigned to be an instance of StringIO. What environment (like inside some web framework, from command line) is this code running under? That might give someone familiar with that environment a clue as to the proper answer.

John Gaines Jr.
  • 11,174
  • 1
  • 25
  • 25