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

问题描述 投票:0回答:1

我有一个边缘案例问题。我的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可能更容易,但我想我会问这里。

python python-3.x subprocess
1个回答
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),在3.3.1及更高版本中是-1(意思是“使用合适的默认缓冲区大小”)。为了表现,明确使用bufsize=-1是一个好主意;无论如何你都在线程读取,所以缓冲死锁不是一个问题。
  2. 切勿使用codecs.open。这是错误的(不翻译行结尾,混合readlineread(n)调用做奇怪的事情,当没有编码通过,它甚至不包括普通open的结果,所以API改变等),慢,和准弃用。如果您需要在Python 2.6及更高版本上保持一致的行为,请使用io.open,它在Python 2.6及更高版本上提供Python 3内置的open函数。
© www.soinside.com 2019 - 2024. All rights reserved.