Python в Windows «Обрабатывать неверно» при перенаправлении записи stdout в файл - PullRequest
0 голосов
/ 17 сентября 2018

Скрипт, который я пытаюсь исправить, использует следующую парадигму для перенаправления 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 и есть ли простой способ исправить это?

1 Ответ

0 голосов
/ 18 сентября 2018

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

Проблема в том, что print выполняет запись в объект 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 (по крайней мере, так же, как консоль может поддерживать его).Для этого он использует консольный API UTF-16 (например, ReadConsoleW и WriteConsoleW).Ранее поддержка консоли CPython была ограничена текстом, который был закодирован с помощью кодовых страниц Windows, с использованием универсального байтового ввода-вывода (например, ReadFile и WriteFile).

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...