2014-12-31 13 views
5

Python 3.4に移行する準備をしており、unicode_literalsを追加しました。私たちのコードは、サブプロセスモジュールを使用して外部ユーティリティとの間でパイプ接続することに大いに依存しています。次のコードスニペットは、サブプロセスへのパイプのUTF-8文字列にはPython 2.7で正常に動作します:Pythonサブプロセスをunicode_literalsに移行するエンコーディングを修正するにはどうすればよいですか?

kw = {} 
kw[u'stdin'] = subprocess.PIPE 
kw[u'stdout'] = subprocess.PIPE 
kw[u'stderr'] = subprocess.PIPE 
kw[u'executable'] = u'/path/to/binary/utility' 
args = [u'', u'-l', u'nl'] 

line = u'¡Basta Ya!' 

popen = subprocess.Popen(args,**kw) 
popen.stdin.write('%s\n' % line.encode(u'utf-8')) 
...blah blah... 

次の変更は、このエラーをスロー:

from __future__ import unicode_literals 

kw = {} 
kw[u'stdin'] = subprocess.PIPE 
kw[u'stdout'] = subprocess.PIPE 
kw[u'stderr'] = subprocess.PIPE 
kw[u'executable'] = u'/path/to/binary/utility' 
args = [u'', u'-l', u'nl'] 

line = u'¡Basta Ya!' 

popen = subprocess.Popen(args,**kw) 
popen.stdin.write('%s\n' % line.encode(u'utf-8')) 
Traceback (most recent call last): 
    File "test.py", line 138, in <module> 
    exitcode = main() 
    File "test.py", line 57, in main 
    popen.stdin.write('%s\n' % line.encode('utf-8')) 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128) 

任意の提案が通過UTF-8を渡しますパイプ?あなたがunicode_literalsを使用する場合

+0

両方で同じに動作するためのおかげこれは、まったく同じ状況と問題を抱えていて、このスレッドは大きな助けとなりました。また、いくつかの単純なケースでは、 'sh.py'(https://github.com/amoffat/shから入手可能)は、'サブプロセス狂気。 – user5359531

答えて

5

'%s\n'は、Unicode文字列です:

>>> line = u'¡Basta Ya!' 
>>> '%s\n' % line.encode(u'utf-8') 
'\xc2\xa1Basta Ya!\n' 
>>> u'%s\n' % line.encode(u'utf-8') 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 0: ordinal not in range(128) 

何が起こることは、あなたのエンコードline値ががUnicode '%s\n'文字列に補間するをデコードされているということです。

バイト文字列を代わりに使用する必要があります。

>>> from __future__ import unicode_literals 
>>> line = u'¡Basta Ya!' 
>>> b'%s\n' % line.encode(u'utf-8') 
'\xc2\xa1Basta Ya!\n' 

または後の補間エンコード:bで文字列の接頭辞のPython 3では

>>> line = u'¡Basta Ya!' 
>>> ('%s\n' % line).encode(u'utf-8') 
'\xc2\xa1Basta Ya!\n' 

を、あなたはとにかくパイプにバイト文字列を記述する必要があります。

+0

'TextIOWrapper()'オプションは、Python 2では動作しないので、もう一度削除しました。パイプオブジェクトは 'io'抽象基底クラスに基づいておらず、ラップは失敗します。 –

+0

優秀!できます。また、別の場所に警告を作成しました。別の質問でそれを取り上げます。ありがとう! – tahoar

+0

'universal_newlines = True'ならPython 3にテキストを書くことができます。 – jfs

4

ロケールのエンコーディングのためのutf-8スタンドは、Unicode文字列を使用して通信する場合は、あなたは、Python 3にuniversal_newlines=Trueを使用することもできます。

#!/usr/bin/env python3 
from subprocess import Popen, PIPE 

p = Popen(['/path/to/binary/utility', '-l', 'nl'], 
      stdin=PIPE, stdout=PIPE, stderr=PIPE, 
      universal_newlines=True) 
out, err = p.communicate('¡Basta Ya!') 

コードはロケールのエンコーディングがUTF-8でない場合でも動作します。入力/出力は、ここではUnicode文字列です(strタイプ)。

サブプロセスは、現在のロケールは、その後(バイトを読み取り/パス)の代わりにバイト文字列を使用して通信されているものutf-8必要とする場合:

#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
from __future__ import unicode_literals 
import os 
from subprocess import Popen, PIPE 

p = Popen(['/path/to/binary/utility', '-l', 'nl'], 
      stdin=PIPE, stdout=PIPE, stderr=PIPE) 
out, err = map(lambda b: b.decode('utf-8').replace(os.linesep, '\n'), 
       p.communicate((u'¡Basta Ya!' + os.linesep).encode('utf-8'))) 

コードは、Python 2および3

関連する問題