我可以设置Python 3.5 subprocess.Popen管道编码吗?

时间:2018-02-27 09:49:21

标签: python python-3.x subprocess

我有边缘案例问题。我的Python script_A.py有这段代码(缩写)。

script_A.py:
from __future__ import unicode_literals
import subprocess

executable = 'sample.exe'

kwargs['bufsize'] = 0
kwargs['executable'] = executable
kwargs['stdin'] = subprocess.PIPE
kwargs['stdout'] = subprocess.PIPE
kwargs['stderr'] = subprocess.PIPE
kwargs['preexec_fn'] = None
kwargs['close_fds'] = False
kwargs['shell'] = False
kwargs['cwd'] = None
kwargs['env'] = None
kwargs['universal_newlines'] = True
kwargs['startupinfo'] = None
kwargs['creationflags'] = 0
if sys.version_info.major == 3 and sys.version_info.minor > 5:
    kwargs['encoding'] = 'utf-8'

args = ['', '-x']

subproc = subprocess.Popen(args, **kwargs)

# service subproc.stdout and subproc.stderr on threads
stdout = _start_thread(_get_stdout, subproc)
stderr = _start_thread(_get_stderr, subproc)

with codecs.open('myutf-8.txt', encoding='utf-8') as fh:
    for line in fh:
        if os.name == 'nt':
            subproc.stdin.write(b'%s\n' % line.rstrip().encode('utf-8'))
        else:
            subproc.stdin.write('%s\n' % line.rstrip()) # OFFENDING LINE BELOW

stdout.join()

此代码始终适用于Windows 8/10和Ubuntu 16.04 / 17.10上的Python 2.7.14和3.6.4。请注意,某些kwargs值在Windows上是不同的,但它们在这里无关紧要。它适用于16.04上的Python 3.5.2,但仅限于我从Gnome终端执行script_A.py时。

有时,我需要使用script_B.py来启动script_A.py而不是终端。 Script_B.py具有相同的subprocess.Popen()代码,用于启动相应的Python可执行文件。

script_B.py
if os.name == 'nt':
    if use_py2:
        executable = 'C:\\Python27\\python.exe'
    else:
        executable = 'C:\\Program Files\\Python36\\python.exe'
else:
    if use_py2:
        executable = '/usr/bin/python'
    else:
        executable = '/usr/bin/python3'

args = ['', 'script_A.py']

# ---- ditto above code from here ----

我在Python 3.5.2上使用Popen()从script_B.py执行script_A.py时出现此错误。 OS / Python版本的其他组合都没有失败。

Traceback:
  File "script_A.py", line 30, in run
    subproc.stdin.write('%s\n' % line.rstrip())
UnicodeEncodeError: 'ascii' codec can't encode characters in position 0-4: ordinal not in range(128)

你可以在2.7.14和3.6.4上看到,我使用特定代码强制管道为utf-8。我不知道如何在3.5.2上设置utf-8编码。

那么,有没有办法在3.5.2 Popen管道上配置编码?将Python 3.5排除在支持之外可能更容易,但我想我会问这里。

1 个答案:

答案 0 :(得分:0)

您的输入文件是UTF-8,并且您要提供数据的程序需要UTF-8输入。所以直接发送原始二进制文件,而不是从字节到文本解码,然后从文本重新编码为字节。

摆脱启用universal_newlines模式的行以及设置kwargs['encoding']的行,并替换为with提供的整个stdin块:

blinesep = os.linesep.encode('utf-8')  # Since you seem to need OS specific line endings
with open('myutf-8.txt', 'rb') as fh:
    for line in fh:
        subproc.stdin.writelines((sline, blinesep))

如果您愿意,您仍然可以将stdout / stderr流作为文本流处理,您只需使用io.TextIOWrapper和适当的编码明确地将它们包装起来。例如,您可以使用:

包装二进制文件stdout
textout = io.TextIOWrapper(subproc.stdout, encoding='utf-8')

一些附注:

  1. 您在调用bufsize时明确设置Popen是正确的,因为如果不这样做,它就不可能在Python版本中保持一致;在Python 2和Python 3.3.0及更早版本上,默认缓冲行为(bufsize=0)和-1(意思是"使用合适的默认缓冲区大小")在3.3.1和后来。为了提高性能,明确使用bufsize=-1是一个好主意;无论如何你都要对读取进行线程处理,因此缓冲死锁并不是一个问题。
  2. 永远不要使用codecs.open。它的错误(没有翻译行结尾,混合readlineread(n)调用奇怪的事情,当没有编码通过时,它没有&#39 ; t甚至包括普通open的结果,因此API改变等等),缓慢和准弃用。如果您需要在Python 2.6及更高版本上保持一致的行为,请使用io.open,它在Python 2.6及更高版本上一致地提供Python 3内置open函数。