2014-12-31 6 views
5

Ci stiamo preparando per passare a Python 3.4 e aggiunto unicode_literals. Il nostro codice si basa ampiamente sulle tubazioni da/verso utility esterne che utilizzano un modulo subprocess. Il seguente frammento di codice funziona bene su Python 2.7 per tubi stringhe UTF-8 a un sotto-processo:Come risolvere una codifica che migra il sottoprocesso Python in 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... 

le seguenti modifiche gettare questo errore:

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) 

Qualsiasi suggerimento per passare UTF-8 a la pipa?

+0

grazie per questo, ho avuto la stessa identica situazione e problema e questo thread è stato di grande aiuto. Ho anche scoperto che in alcuni casi semplici, 'sh.py' (disponibile qui: https://github.com/amoffat/sh) è stato un sostituto drop-in facile, portatile e compatibile per alcuni dei sottoprocessi. follia. – user5359531

risposta

5

'%s\n' è una stringa Unicode quando si utilizza unicode_literals:

>>> 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) 

Quello che succede è che il vostro valore codificato line è essere decodificato interpolare nel '%s\n' stringa Unicode.

È necessario utilizzare una stringa di byte ; prefisso la stringa con b:

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

o codificare dopo interpolazione:

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

In Python 3, dovrete scrivere stringhe di byte da tubo in ogni caso.

+0

Ho rimosso di nuovo l'opzione 'TextIOWrapper()' poiché non funzionerà in Python 2; gli oggetti pipe non sono basati sulle classi di base astratte 'io' e il wrapping fallisce. –

+0

Eccellente! Funziona. Ha anche creato un avvertimento in un altro posto. Lo affronterò in un'altra domanda. Grazie! – tahoar

+0

se 'universal_newlines = True', potresti scrivere testo su Python 3. – jfs

4

Se utf-8 stand per la vostra codifica locale poi di comunicare usando stringhe Unicode, è possibile utilizzare universal_newlines=True su Python 3:

#!/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!') 

il codice funziona anche se la codifica del locale non è UTF-8. Input/output sono stringhe Unicode qui (tipo str).

Se il processo richiede utf-8 indipendentemente dalla locale corrente viene quindi comunicare utilizzando stringhe di byte invece (passa/lettura byte):

#!/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'))) 

Il codice funziona lo stesso su entrambi Python 2 e 3.

Problemi correlati