Как подпроцесс решает, какую кодировку записывать в файловые объекты, которые принимают только str

#python #subprocess

#python #подпроцесс

Вопрос:

Я обнаружил следующее поведение довольно запутанным:

 Python 3.7.2 (default, Feb 12 2019, 08:15:36) 
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import subprocess
>>> import sys
>>> sys.stdout.write(b'')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: write() argument must be str, not bytes
>>> subprocess.run('echo', stdout=sys.stdout)

CompletedProcess(args='echo', returncode=0)
  

sys.stdout не принимает двоичный файл. Я не указывал кодировку в своем subprocess вызове, это означает, что она должна быть потоковой двоичной. Как подпроцесс узнал, что в этом случае не нужно передавать двоичные данные файлового объекта?

Ответ №1:

Вы неправильно понимаете, как работают подпроцессы. Подпроцесс не взаимодействует с sys.stdout объектом — этот объект существует только в python и только в вашем процессе.

Чтобы понять, что происходит на самом деле, вам сначала нужно знать, как ОС обрабатывает ввод-вывод. На уровне операционной системы каждому открытому файлу (или каналу) присваивается идентификатор — это называется дескриптором файла. Например, дескриптором для stdout обычно является число 1 :

 >>> sys.stdout.fileno()
1
  

Когда вы запускаете подпроцесс, в подпроцесс передается только этот дескриптор файла. Подпроцесс не имеет доступа к sys.stdout файловому объекту. Все, что может сделать подпроцесс, это записать байты в полученный дескриптор файла. (На уровне ОС есть только байты, текста нет.) Вы не можете заставить подпроцесс использовать определенную кодировку.

Когда вы передаете encoding аргумент subprocess.run , эта кодировка используется только для кодирования текста, который вы отправляете в подпроцесс или получаете от подпроцесса. Это не влияет на сам подпроцесс, это влияет только на то, как ваш процесс взаимодействует с подпроцессом.

Комментарии:

1. Фактически, вы не можете передать какой-либо файловый объект, не основанный на файловых дескрипторах, в подпроцесс; например, subprocess.run('echo', stdout=io.BytesIO()) произойдет сбой с io.UnsupportedOperation: fileno

2. Еще одним интересным следствием этого факта является то, что буфер передаваемого вами файлового объекта неявно очищается. Поэтому, если у вас есть print("A", end="", file=sys.stdout); subprocess.run (("echo", "B"), stdout=sys.stdout) , A не будет сброшен до запуска подпроцесса.