开发者

AttributeError: StringIO instance has no attribute 'fileno'

开发者 https://www.devze.com 2023-03-02 22:59 出处:网络
def captureOutput(self, func, *args, **kwargs): pass sys.stdout.flush() sys.stderr.flush() (outfd, fn) = tempfile.mkstemp()
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())
            o开发者_开发知识库s.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')

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


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.


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.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号