AttributeError: StringIO instance has no attribute 'fileno'

匿名 (未验证) 提交于 2019-12-03 01:29:01

问题:

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?

回答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.

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 


回答2:

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


回答3:

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.



回答4:

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.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!