Подтвердить что ты не робот

AttributeError: экземпляр StringIO не имеет атрибута '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'

Почему я получаю эту ошибку и как ее исправить?

4b9b3361

Ответ 1

Вы используете стандартный простой интерпретатор python? Эта ошибка может появиться при использовании интерпретатора, который переопределяет stdout/stderr, например IDLE (хотя IDLE сам по себе дал бы вам другую ошибку). Это может также быть вызвано библиотекой, которая переопределяет stdout/stderr.

Иногда вы можете reset создать stdout стандартное stdout, написав sys.stdout = sys.__stdout__, но не рассчитывайте на то, что он работает всегда. Например, он не работает в Pythonwin.

В любом случае, похоже, что вы пытаетесь сделать с вашим кодом, это перенаправить stdout/stderr самостоятельно. Если это так, вы должны просто пойти и сделать это. Я думаю, что это должно сработать, если у вас есть файловые дескрипторы outfd и errfd:

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

Метод fileno() не реализован в StringIO, так как он не является реальным файлом (поэтому не имеет связанного файлового дескриптора). Из источника:

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

Возможно, кто-то заменил sys.stdout экземпляром StringIO, чтобы захватить вывод.

Например, когда я запускаю свой код таким образом, я получаю одно и то же исключение:

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

Ответ 3

Мое предположение было бы где-то еще в коде, sys.stdout или sys.stderr было переназначено, чтобы быть экземпляром StringIO. Какая среда (например, внутри какой-либо веб-структуры, из командной строки) работает под этим кодом? Это может дать кому-то знакомый с этой средой ключ к правильному ответу.

Ответ 4

Короткий ответ: вы столкнулись с ошибкой в ​​стандартной библиотеке. StringIO не выполняет контракт с базовым классом IOBase. Некоторый класс написал интерфейс класса IOBase, который затем терпит неудачу.

Более конкретно, Subprocess.run() или некоторая другая функция использовала функцию fileno IOBase. Подкласс StringIO выдает это исключение, потому что это не настоящий подкласс. Где-то один из многих пользователей IOBase терпит неудачу. Документирование StringIO не помогает этой проблеме.

Вы можете закодировать вокруг него, может быть. А может и нет. Всевозможные функции, такие как contextlib.redirect_stdout(), не будут выполнены.