Os.device_encoding изменяется при вызове из подпроцесса, что приводит к ошибке декодирования в Windows. Как принудительнPython

Программы на Python
Ответить
Anonymous
 Os.device_encoding изменяется при вызове из подпроцесса, что приводит к ошибке декодирования в Windows. Как принудительн

Сообщение Anonymous »

У меня есть следующий код Python 3.7 в импортированном пакете, который я не могу изменить, который считывает и декодирует имя системного компилятора, предоставленное disttools:

subproc.py

import os
import sys
import locale
import subprocess
from distutils.ccompiler import new_compiler

ccompiler = new_compiler()
ccompiler.initialize()
cc = subprocess.check_output(f"{ccompiler.cc}", stderr=subprocess.STDOUT, shell=True)
encoding = os.device_encoding(sys.stdout.fileno()) or locale.getpreferredencoding()
print("Encoding:", encoding)
compiler_name = cc.decode(encoding).partition("\n")[0].strip()
print("Compiler name:", compiler_name)

При прямом вызове, т. е. непосредственном запуске subprocess.py, все работает нормально. Имя компилятора правильно идентифицируется как «Microsoft (R) C/C++-Optimierungscompiler Version 19.42.34433 für x64» (я думаю, причина проблемы в ü)
Однако, когда я вызываю его с помощью subprocess.Popen(), os.device_encoding возвращает None вместо cp850, вызывая программа по умолчанию использует кодировку Windows cp1252, что затем приводит к тому, что cc.decode(encoding) вызывает "UnicodeDecodeError: кодек 'charmap' не может декодировать байт 0x81 в позиции 62: символ сопоставляется с ".
Вот как я запускаю подпроцесс:

call_subprocess.py

import subprocess

subprocess.Popen(
[
"python",
"C:/path/to/subproc.py",
],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)

Насколько я понимаю, os.device_encoding(sys.stdout.fileno()) не может найти кодировку, поскольку подпроцесс выполняется в фоновом режиме без терминала. Кроме того, Windows всегда будет предоставлять cp1252 при запросе с помощью locale.getpreferredencoding().
Поскольку я не могу редактировать код во внешнем пакете, есть ли способ вызвать подпроцесс, чтобы заставить любую из этих команд вернуть cp850?
Варианты, которые я пытался решить проблему
  • Явно установите кодировку в Popen:
    subprocess.Popen(
    ...
    text=True,
    encoding="cp850",
    )
  • Явно установите PYTHONIOENCODING в среде подпроцесса:
    environ = os.environ.copy()
    environ['PYTHONIOENCODING'] = 'utf-8'
    ...
    subprocess.Popen(
    ...
    env=environ,
    encoding='utf-8',
    )
  • Используйте subprocess.run() вместо subprocess.Popen()
  • Различные комбинации приведенных выше решений.
Ресурсы, которые я уже просматривал
  • Подпроцесс использует неверную кодировку в Windows
  • Ошибка кодирования, выполняемая в подпроцессе с захваченным вывод
  • Изменение предпочтительной кодировки локали для самого компьютера: Панель управления > Часы и регион > Регион > Администрирование > Изменить языковой стандарт системы > Проверить бета-версию: использовать Unicode UTF-8 > Перезагрузка -> Работает , но это нежелательно, так как код должен быть исполняемым на разных машинах без индивидуальной настройки каждый раз.
  • Поскольку в моем случае подпроцесс был достаточно изолирован от другого кода и имел собственную функцию запуска, я использовал следующие строки перед первый импорт локали для переопределения возвращаемого значения locale.getdefaultlocale() (Источник):
# Override locale.getdefaultlocale() for the subprocess.
# This is necessary to avoid issues with the default locale on Windows.
# It might cause issues on computers not in western countries, that do not use cp850.
import _locale

_locale._getdefaultlocale = lambda *args: ["en_US", "cp850"]


Подробнее здесь: https://stackoverflow.com/questions/793 ... ding-error
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Python»