簡體   English   中英

AttributeError:StringIO實例沒有屬性'fileno'

[英]AttributeError: StringIO instance has no attribute 'fileno'

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'

為什么我會收到此錯誤,如何更正錯誤?

fileno()方法未在StringIO中實現,因為它不是真實文件(因此沒有關聯的文件描述符)。 從來源:

- fileno() is left unimplemented so that code which uses it 
triggers an exception early.

有人可能會用StringIO實例替換sys.stdout來捕獲輸出。

例如,當我以這種方式運行您的代碼時,我得到相同的異常:

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

錯誤:

    oldstdout = os.dup(sys.stdout.fileno())
AttributeError: StringIO instance has no attribute 'fileno'

最好從端到端跟蹤代碼,尋找覆蓋sys.stdout點。 這是我給出的另一個答案鏈接 ,顯示了如何通過跟蹤活動來執行代碼:

ares% python -m trace -c -t -C ./coverage test_sio.py | grep sys.stdout
test_sio.py(47): sys.stdout = StringIO()

你使用標准的普通python解釋器嗎? 當您使用覆蓋stdout / stderr的解釋器(例如IDLE)時可能會出現此錯誤(盡管IDLE本身會給您一個不同的錯誤)。 它也可能是由覆蓋stdout / stderr的庫引起的。

有時您可以通過編寫sys.stdout = sys.__stdout__將stdout重置為默認標准輸出,但不要指望它始終有效。 例如,它在Pythonwin中不起作用。

無論如何,您嘗試對代碼執行的操作似乎是自己重定向stdout / stderr。 如果是這樣的話,你應該繼續這樣做。 我認為這應該outfd ,如果你有文件描述符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

簡短的回答是您遇到了標准庫中的錯誤。 StringIO不符合其IOBase基類的合同。 有些類寫入IOBase類接口,然后失敗。

更具體地說,Subprocess.run()或其他一些函數使用了IOBase fileno函數。 子類StringIO拋出此異常,因為它不是真正的子類。 在某個地方,IOBase的許多用戶之一失敗了。 記錄StringIO無助於解決此問題。

你也可以圍繞它編碼。 或者可能不是。 contextlib.redirect_stdout()等各種函數都將失敗。

我的猜測是代碼中的其他地方,sys.stdout或sys.stderr被重新分配為StringIO的一個實例。 這個代碼運行在什么環境(比如某些Web框架,來自命令行)? 這可能會讓熟悉該環境的人知道正確的答案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM