Python в Windows «Обрабатывать неверно» при перенаправлении записи stdout в файл

1

Сценарий, который я пытаюсь исправить, использует следующую парадигму для перенаправления stdout в файл.

import os
stdio_file = 'temp.out'
flag = os.O_WRONLY | os.O_CREAT | os.O_TRUNC
stdio_fp = os.open(stdio_file, flag)
os.dup2(stdio_fp, 1)
print("hello")

На Python 2 это работает. На Python 3 вы получаете OSError

Traceback (most recent call last):
  File "test.py", line 6, in <module>
    print("hello")
OSError: [WinError 6] The handle is invalid
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
OSError: [WinError 6] The handle is invalid

Я предполагаю, что есть более предпочтительные методы для маршрутизации stdout через файл, но мне интересно, почему этот метод перестает работать в Python 3 и если есть простой способ его исправить?

  • 0
    @eryksun Ваши комментарии - довольно хороший ответ. Почему бы не сделать это в одном?
  • 0
    Вы правы, настройка PYTHONLEGACYWINDOWSSTDIO устраняет проблему. Тем не менее, я все еще пытаюсь проработать дополнительный комментарий в моей голове. Я вроде незнаком со многими вещами ОС.
Теги:
python-3.x
operating-system

1 ответ

1
Лучший ответ

Код, например os.dup2(stdio_fp, 1) будет работать в Python 3.5 и ранее, или в 3. 6+ с переменной среды PYTHONLEGACYWINDOWSSTDIO.

Проблема заключается в том, что print записывается в объект sys.stdout который предназначен только для консольных sys.stdout ввода-вывода. В частности, в 3. 6+ исходный слой стандартного выходного файла Python 3 (т.е. sys.stdout.buffer.raw) является экземпляром io._WindowsConsoleIO когда stdout изначально представляет собой файл консоли 1. Этот объект кэширует начальное значение дескриптора дескриптора файла stdout 2. Впоследствии dup2 закрывает этот дескриптор, повторно связав дескриптор файла с дублирующимся дескриптором для "temp.out". На этом этапе кешированная ручка больше не действует. (На самом деле, он не должен кэшировать дескриптор, так как вызов _get_osfhandle относительно дешев по сравнению с затратами на консольный ввод-вывод.) Однако, даже если у него был действительный дескриптор для "temp.out", sys.stdout.write так или иначе, поскольку _WindowsConsoleIO использует только консольную функцию WriteConsoleW вместо общего WriteFile.

Вам нужно переназначить sys.stdout вместо обхода стека ввода-вывода Python с помощью операций низкого уровня, таких как dup2. Я знаю, что он не идеален с точки зрения разработчиков Unix. Хотелось бы, чтобы мы могли повторно реализовать способ поддержки Unicode для консоли Windows, не представляя этот консольный класс _WindowsConsoleIO, что нарушает низкоуровневые шаблоны, на которые люди полагались десятилетиями.


1. _WindowsConsoleIO был добавлен для поддержки всего диапазона Unicode в консоли Windows (по крайней мере, так же как консоль может его поддерживать).Для этого он использует ReadConsoleW API-интерфейс консоли UTF-16 (например, ReadConsoleW и WriteConsoleW).Раньше поддержка консоли CPython ограничивалась текстом, который был закодирован с кодовыми страницами Windows, используя общие байтовые ReadFile ввода-вывода (например, ReadFile и WriteFile).

2. Windows использует дескрипторы для ссылки на объекты ядра, такие как объекты File.Эта система несовместима с поведением с файловыми дескрипторами POSIX (FD).Таким образом, среда выполнения C (CRT) имеет уровень совместимости с "низким уровнем ввода-вывода", который связывает FD файлы в стиле POSIX с файлами Windows, а также реализует функции ввода-вывода POSIX, такие как open и write.Функция _open_osfhandle связывает собственный дескриптор файла с FD, а _get_osfhandle возвращает дескриптор, связанный с FD.Иногда CPython использует низкий уровень ввода-вывода CRT, и иногда он напрямую использует Windows API.Это действительно беспорядок, если вы спросите меня.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню