2012-02-22 3 views
7

Desidero inviare messaggi di posta elettronica con corpi unicode arbitrari in un programma Python 3.2. Ma, in realtà, questi messaggi consisteranno in gran parte di testo ASCII a 7 bit. Quindi mi piacerebbe che i messaggi fossero codificati in utf-8 usando il quoted-printable. Finora, ho trovato questo funziona, ma sembra sbagliato:Come utilizzare il modulo email Python 3.2 per inviare messaggi Unicode codificati in utf-8 con quoted-printable?

c = email.charset.Charset('utf-8') 
c.body_encoding = email.charset.QP 
m = email.message.Message() 
m.set_payload("My message with an '\u05d0' in it.".encode('utf-8').decode('iso8859-1'), c) 

Ciò si traduce in un messaggio di posta elettronica con esattamente il contenuto giusto:

To: [email protected] 
From: [email protected] 
Subject: This is a subjective subject. 
MIME-Version: 1.0 
Content-Type: text/plain; charset="utf-8" 
Content-Transfer-Encoding: quoted-printable 

My message with an '=D7=90' in it. 

In particolare b'\xd7\x90'.decode('utf-8') risultati nel carattere originale Unicode . Quindi la codifica quoted-printable sta correttamente rendendo il utf-8. Sono consapevole che si tratta di un trucco incredibilmente brutto. Ma funziona.

Questo è Python 3. Le stringhe di testo dovrebbero essere sempre unicode. Non dovrei decodificarlo in utf-8. E poi trasformarlo da bytes di nuovo in str da .decode('iso8859-1') è un attacco orribile e non dovrei dovere fare neanche quello.

E 'il modulo email appena rotto rispetto alle codifiche? Non sto ottenendo qualcosa?

Ho appena provato a impostarlo, senza set di caratteri. Questo mi lascia con un messaggio e-mail unicode, e non è affatto giusto. Ho anche provato a lasciare i passi encode e decode. Se li lascio entrambi, si lamenta che lo \u05d0 è fuori portata quando si tenta di decidere se quel carattere deve essere citato nella codifica quoted-printable. Se lascio solo il passaggio encode, si lamenta amaramente di come sto passando in un bytes e vuole un str.

+0

Se ' "Il mio messaggio con un '\ u05d0' in esso."' È l'unicode che desideri, allora non è possibile utilizzare '" Il mio messaggio con un '\ u05d0' in it. ". encode ('utf-8'). decode ('iso8859-1')' poiché questo un unicode differente. (Avrai modificato il messaggio.) – unutbu

+0

@unutbu: Congratulazioni per aver scoperto perché il codice è molto brutto. Ma funziona. Raggiunge il risultato desiderato. Vedi il mio aggiornamento. – Omnifarious

risposta

8

Quel pacchetto di posta elettronica non è confuso su quale sia (codificato unicode rispetto a dati binari codificati con trasferimento del contenuto), ma la documentazione non lo rende molto chiaro, poiché gran parte della documentazione risale a un'epoca in cui "codifica" "significava codifica per il trasferimento di contenuto. Stiamo lavorando su un'API migliore che renderà tutto più facile da usare (e documenti migliori).

Esiste davvero un modo per far sì che il pacchetto di posta elettronica utilizzi QP per i corpi utf-8, ma non è molto ben documentato. Lo si fa in questo modo:

>>> charset.add_charset('utf-8', charset.QP, charset.QP) 
>>> m = MIMEText("This is utf-8 text: á", _charset='utf-8') 
>>> str(m) 
'Content-Type: text/plain; charset="utf-8"\nMIME-Version: 1.0\nContent-Transfer-Encoding: quoted-printable\n\nThis is utf-8 text: =E1' 
+0

Grazie! Questo risponde alla mia domanda perfettamente e mi dà un modo per fare ciò che voglio che non sia un hack inquietante. :-) – Omnifarious

+1

Che gestisce il tuo personaggio bene. Ma non gestisce il carattere \ u05d0. Infatti, non codifica il tuo personaggio come utf-8, lo codifica come iso8859-1. : -/ – Omnifarious

+0

Vedere anche Python [issue1525919] (http://bugs.python.org/issue1525919#msg29229). – mmoya

1

Esecuzione

import email 
import email.charset 
import email.message 

c = email.charset.Charset('utf-8') 
c.body_encoding = email.charset.QP 
m = email.message.Message() 
m.set_payload("My message with an '\u05d0' in it.", c) 
print(m.as_string()) 

rendimenti questo messaggio traceback:

File "/usr/lib/python3.2/email/quoprimime.py", line 81, in body_check 
    return chr(octet) != _QUOPRI_BODY_MAP[octet] 
KeyError: 1488 

Dal

In [11]: int('5d0',16) 
Out[11]: 1488 

è chiaro che l'unicode '\u05d0' è il personaggio problema. _QUOPRI_BODY_MAP è definita da quoprimime.py

_QUOPRI_HEADER_MAP = dict((c, '=%02X' % c) for c in range(256)) 
_QUOPRI_BODY_MAP = _QUOPRI_HEADER_MAP.copy() 

Questo dict contiene solo chiavi da range(256). Quindi penso che tu abbia ragione; quoprimime.py non può essere utilizzato per codificare unicode arbitrario.

Per aggirare il problema, è possibile utilizzare (il default) base64 omettendo

c.body_encoding = email.charset.QP 

Si noti che la latest version di quoprimime.py non usa _QUOPRI_BODY_MAP a tutti, in modo da utilizzare l'ultima Python potrebbe risolvere il problema.

+2

Sospetto che non lo farà. Il problema sembra non essere correttamente convertito in utf-8 byte prima di applicare la codifica quoted-stampabile. I metodi 'as_string' e' __str__' di 'email.message.Message' dovrebbero essere deprecati a favore dei metodi che restituiscono invece i byte. Immagino che l'intero pacchetto email sia un po 'confuso sulla differenza tra la codifica binaria eseguita su un messaggio di posta elettronica e la codifica implicita dall'uso di un particolare sistema di codifica dei caratteri. Questi due sono in realtà concetti separati anche se entrambi usano il termine "codifica". – Omnifarious