Почему я получаю ValueError при явном закрытии stdout?

Питон новичок здесь. Я пишу скрипт, который может выводить некоторый вывод либо в файл, либо в стандартный вывод, в зависимости от переданных ему аргументов. При интерпретации аргументов я присваиваю файл open или stdout глобальной переменной с именем output_file, которая может использоваться остальной частью скрипта для записи вывода независимо от того, какой тип потока был выбран. В самом конце сценария я close output_file. Это правильно для файлового потока, и хотя это избыточно для stdout, мой опыт работы с другими языками программирования показывает, что нет вреда в явном закрытии stdout непосредственно перед завершением программы.

Однако всякий раз, когда стандартный вывод используется для вывода (и впоследствии закрывается), я получаю «ValueError: 'Операция ввода-вывода в закрытом файле'». Я знаю, что эта ошибка не вызвана моим вызовом close stdout напрямую, но возникает после возврата моего сценария. Мой вопрос: почему это происходит, и есть ли способ вручную закрыть стандартный вывод, не запуская его? (Я знаю, что могу легко обойти проблему, условно закрывая поток только тогда, когда файл был выбран, но я хочу знать, нужно ли/почему это необходимо.)

Очень простой демонстрационный фрагмент:

from sys import stdout
stdout.close()

person nonoitall    schedule 05.02.2012    source источник
comment
Пожалуйста, проверьте свой код на любые операции с output_file после закрытия stdout() или добавьте сюда больше кода, так как данный фрагмент кода может вызвать ValueError только в том случае, если output_file.write(..) был использован после фрагмента.   -  person Vladimir    schedule 05.02.2012
comment
Данный фрагмент выдает ошибку сам по себе.   -  person nonoitall    schedule 05.02.2012


Ответы (4)


Проблема в том, что на python-3.2 есть попытка выключения сбросить стандартный вывод без проверки того, был ли он закрыт.

issue13444 как раз об этом.

У вас не должно быть этой проблемы в python-2.7 выпусках после исправления.

person Rik Poggi    schedule 05.02.2012
comment
Рад слышать, что не все из-за того, что я не знаю Python. Спасибо что подметил это. :-) - person nonoitall; 05.02.2012

После того, как вы закрыли stdout таким образом, вы должны быть абсолютно уверены, что ничто не попытается напечатать что-либо в stdout. Если что-то произойдет, вы получите это исключение.

Я бы рекомендовал закрыть output_file условно:

if output_file != sys.stdout:
    output_file.close()

edit Вот пример, где sys.stdout закрывается прямо в самом конце скрипта, и тем не менее при запуске выдает ValueError: 'I/O operation on closed file.

import atexit

@atexit.register
def goodbye():
    print "You are now leaving the Python sector."

import sys
sys.stdout.close()
person NPE    schedule 05.02.2012
comment
Ничего в моем сценарии не выводится на стандартный вывод после вызова, чтобы закрыть его. (Призыв закрыть его — самая последняя строка в сценарии.) Есть ли что-то еще, что я могу сделать, чтобы этот «таинственный преступник» не использовал стандартный вывод после того, как я его закрыл? :-П - person nonoitall; 05.02.2012
comment
@nonoitall: Конечно, вы можете подавить его (например, назначив stdout какому-то потоку, который отбрасывает все, что в него записано). Однако, учитывая, что на данный момент вы даже не знаете, что пишет stdout, слепое отбрасывание этого вывода кажется довольно глупым. - person NPE; 05.02.2012
comment
кто-нибудь знает, что пишет на стандартный вывод? Даже если часть atexit удалена из вашего примера сценария, ошибка все равно возникает. (См. мой фрагмент.) Единственной строкой во всем сценарии может быть вызов закрытия стандартного вывода, и ошибка все равно будет выдана. /me задается вопросом, почему. - person nonoitall; 05.02.2012
comment
@nonoitall: Что происходит, когда вы запускаете скрипт, состоящий только из import sys? Дает ли это какой-либо результат? (Если вы работаете в Unix, запустите python xyz.py | wc, чтобы увидеть, получили ли вы все нули, где xyz.py — рассматриваемый скрипт). - person NPE; 05.02.2012
comment
Я работаю в Windows, поэтому у меня нет туалета под рукой, но скрипт только с import sys не дал результата. (Перенаправил его стандартный вывод в файл через командную строку, и полученный файл был полностью пуст.) - person nonoitall; 05.02.2012

Перед закрытием вы можете проверить файл output_file.closed:

if not output_file.closed:
    output_file.close()

И убедитесь, что у вас нет вызовов ввода-вывода для output_file после закрытия.

person Vladimir    schedule 05.02.2012
comment
Не решает проблему. Мой вызов close stdout не является прямой причиной ошибки; stdout открыт, когда я его закрываю. После того, как мой скрипт возвращается, что-то еще пытается использовать поток, вызывая ошибку. Мне интересно, что это за что-то еще, и есть ли способ подавить это. - person nonoitall; 05.02.2012

Чтобы избежать этой ошибки, кажутся необходимыми две вещи: reset (i) reset stdout; (ii) не закрывайте стандартный вывод, закройте файл, на который он был перенаправлен.

f=open(filename, 'w')
sys.stdout = f
print("la la-la"), file = sys.stdout)
f.close()
sys.stdout = sys.__stdout__

Различные решения этой проблемы предлагают скопировать «исходный» указатель stdout в переменную перед назначением stdout файлу (т. е. original = stdout ... stdout = f), а затем скопировать его обратно (stdout = original). Но они забывают упомянуть последнюю операцию в своей рутине, то есть потраченные впустую часы на выдергивание волос.

Решение найдено здесь.

person markling    schedule 22.10.2016