AttributeError:StringIO实例没有属性'fileno'

时间:2011-05-05 20:10:03

标签: 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 

运行此代码时,出现错误:

AttributeError: StringIO instance has no attribute 'fileno'

为什么我会收到此错误,如何更正?

4 个答案:

答案 0 :(得分:13)

{1}}方法未在StringIO中实现,因为它不是真实文件(因此没有关联的文件描述符)。来自消息来源:

fileno()

有人可能会使用StringIO实例替换- fileno() is left unimplemented so that code which uses it triggers an exception early. 来捕获输出。

例如,当我以这种方式运行代码时,我得到了相同的异常:

sys.stdout

错误:

from StringIO import StringIO
sys.stdout = StringIO()
captureOutput(testfunc)

最好从头到尾跟踪代码,寻找覆盖 oldstdout = os.dup(sys.stdout.fileno()) AttributeError: StringIO instance has no attribute 'fileno' 的点。这是一个link to another answer I gave,显示如何使用跟踪活动来执行代码:

sys.stdout

答案 1 :(得分:5)

您使用的是标准的普通python解释器吗?当您使用覆盖stdout / stderr的解释器(例如IDLE)时可能会出现此错误(尽管IDLE本身会给您一个不同的错误)。它也可能是由一个覆盖stdout / stderr的库引起的。

有时您可以通过编写sys.stdout = sys.__stdout__将stdout重置为默认标准输出,但不要指望它始终有效。例如,它在Pythonwin中不起作用。

无论如何,您尝试对代码执行的操作似乎是自己重定向stdout / stderr。如果是这样的话,你应该继续这样做。如果您有文件描述符outfderrfd

,我认为这应该有用
sys.stdout = os.fdopen(outfd, 'w')
sys.stderr = os.fdopen(errfd, 'w')

编辑:

现在我可以看到你的整个代码,我根本不会使用临时文件。

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 :(得分:1)

我的猜测是代码中的其他地方,sys.stdout或sys.stderr被重新分配为StringIO的一个实例。这个代码运行在什么环境(比如某些Web框架,来自命令行)?这可能会让熟悉该环境的人知道正确的答案。

答案 3 :(得分:1)

简短的回答是您遇到了标准库中的错误。 StringIO不符合其IOBase基类的合同。有些类写入IOBase类接口,然后失败。

更具体地说,Subprocess.run()或其他一些函数使用了IOBase fileno函数。子类StringIO抛出此异常,因为它不是真正的子类。在某个地方,IOBase的许多用户之一失败了。记录StringIO对此没有帮助。

你可以围绕它编码。或者可能不是。像contextlib.redirect_stdout()这样的各种函数都会失败。