2012-10-10 6 views
6

Quando scrivo gli script sysadmin in Python, il buffer su sys.stdout che effettua ogni chiamata a print() è fastidioso, perché non lo faccio Voglio aspettare che un buffer venga svuotato e poi ottenere una grande quantità di righe contemporaneamente sullo schermo, invece voglio ottenere linee di output individuali non appena viene generato un nuovo output dallo script. Non voglio nemmeno aspettare le newline, quindi guarda l'output.L'idioma standard di Python per impostare il buffer sys.stdout su zero non funziona con Unicode

Un linguaggio spesso usato per fare questo in Python è

import os 
import sys 
sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0) 

questo ha funzionato bene per me per molto tempo. Ora ho notato che non funziona con Unicode. Si prega di consultare il seguente script:

#!/usr/bin/python 
# -*- coding: utf-8 -*- 

from __future__ import print_function, unicode_literals 

import os 
import sys 

print('Original encoding: {}'.format(sys.stdout.encoding)) 
sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0) 
print('New encoding: {}'.format(sys.stdout.encoding)) 

text = b'Eisb\xe4r' 
print(type(text)) 
print(text) 

text = text.decode('latin-1') 
print(type(text)) 
print(text) 

Questo porta alla seguente output:

Original encoding: UTF-8 
New encoding: None 
<type 'str'> 
Eisb▒r 
<type 'unicode'> 
Traceback (most recent call last): 
    File "./export_debug.py", line 18, in <module> 
    print(text) 
UnicodeEncodeError: 'ascii' codec can't encode character u'\xe4' in position 4: ordinal not in range(128) 

Mi ci sono volute ore per rintracciare il motivo per esso (il mio script originale era molto più lungo di questo script di debug minimo). È la linea

sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0) 

che ho usato per anni quindi non mi aspettavo alcun problema con esso. Basta commentare questa linea e l'uscita corretta dovrebbe essere simile a questo:

Original encoding: UTF-8 
New encoding: UTF-8 
<type 'str'> 
Eisb▒r 
<type 'unicode'> 
Eisbär 

Allora, qual è lo script mento di fare? Per preparare il mio codice Python 2.7 il più vicino possibile a Python 3.x, sto utilizzando sempre

from __future__ import print_function, unicode_literals 

che rende pitone utilizzare il nuovo print() - Funzione ma più importante: rende Python negozio tutte le stringhe come Unicode internamente per impostazione predefinita. Ho un sacco di Latin-1/ISO-8859-1 codifica dei dati, ad esempio

text = b'Eisb\xe4r' 

di lavorare con essa il modo previsto, ho bisogno di decodificarlo in Unicode prima, questo è quello che

text = text.decode('latin-1') 

è per. Poiché la codifica predefinita è UTF-8 sul mio sistema, ogni volta che stampo una stringa, python codifica la stringa Unicode interna in UTF-8. Ma prima deve essere in perfetto Unicode internamente.

Ora che tutto funziona bene in generale, non solo con un buffer di output a byte zero finora. Qualche idea? Ho notato che sys.stdout.encoding non è impostato dopo la linea di buffering zero, ma non so come impostarlo di nuovo. È un attributo di sola lettura e le variabili di ambiente del sistema operativo LC_ALL o LC_CTYPE sembrano essere valutate solo all'inizio dell'interprete python.

Btw .: "Eisbär" è la parola tedesca per "orso polare".

+0

@martineau Bene, anche la proposta sys.stdout = codecs.getwriter ('utf8') (sys.stdout) non funziona. Ho davvero provato e cercato molto. Quindi immagino che le idee senza averle testate non siano di grande aiuto. –

+0

Ho migrato la domanda per te. La prossima volta, basta "flag" per l'attenzione del moderatore e dicci cosa ti serve! :) – slhck

+0

@MartenLehmann: Il fatto che non sia stato testato è il motivo per cui l'ho postato come commento piuttosto come una risposta. – martineau

risposta

6

La funzione di stampa utilizza un flag speciale durante la scrittura su un oggetto file, causando la funzione PyFile_WriteObject dell'API Python C per recuperare la codifica di output per eseguire la conversione da Unicode a byte e sostituendo il flusso stdout perso la codifica. Purtroppo, non è possibile impostare in modo esplicito di nuovo:

encoding = sys.stdout.encoding 
sys.stdout = os.fdopen(sys.stdout.fileno(), 'wb', 0) 
sys.stdout.encoding = encoding # Raises a TypeError; readonly attribute 

Non è inoltre possibile utilizzare il io.open function invece, dal momento che non consente il buffering per essere disabilitato se si vuole essere in grado di utilizzare l'opzione encoding avresti bisogno.

Il modo corretto per avere immediatamente la funzione di filo di stampa è quello di utilizzare la parola chiave flush=True:

print(something, flush=True) 

Se questo è troppo noioso per aggiungere ovunque, è consigliabile utilizzare un personalizzato funzione di di stampa:

def print(*args, **kw): 
    flush = kw.pop('flush', True) # Python 2.7 doesn't support the flush keyword.. 
    __builtins__.print(*args, **kw) 
    if flush: 
     sys.stdout.flush() 

Poiché la funzione print() di Python 2.7 in realtà non supporta la parola chiave di livello ancora (disturbo), è possibile simulare che aggiungendo un flush esplicito invece in quella versione personalizzata.

+0

Puoi sostituire tre prime linee della tua funzione 'print()' personalizzata con: 'flush = kw.pop ('flush', True)'. – Tadeck

+0

@Tadeck: ottimo suggerimento, aggiunto. –

Problemi correlati