2012-02-22 10 views
7

Chcę wysyłać wiadomości e-mail z dowolnymi ciałami unicode w programie Python 3.2. Ale w rzeczywistości wiadomości te będą składały się głównie z tekstu ASCII 7-bitowego. Chciałbym więc, aby wiadomości zakodowane w utf-8 używały opcji quoted-printable. Do tej pory znalazłem to działa, ale wydaje się źle:Jak używać modułu e-mailowego Python 3.2 do wysyłania wiadomości w formacie Unicode zakodowanych w utf-8 za pomocą opcji 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) 

Prowadzi to do wiadomości e-mail z dokładnie prawej treści:

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. 

W szczególnych b'\xd7\x90'.decode('utf-8') wyników w oryginalnym znaku Unicode . Tak więc kodowanie quoted-printable prawidłowo renderuje utf-8. Doskonale wiem, że to niesamowicie brzydki hack. Ale działa.

To jest Python 3. Łańcuchy tekstowe powinny zawsze być w Unicode. Nie powinienem był dekodować go do UTF-8. A następnie przekształcenie go z bytes z powrotem w str przez .decode('iso8859-1') jest okropnym hackerem, i nie powinienem był tego robić.

Czy moduł email właśnie pękł w odniesieniu do kodowania? Czy nie dostaję czegoś?

Próbowałem po prostu ustawić go bez zestawu znaków. To pozostawia mi wiadomość e-mail w formacie Unicode, a to wcale nie jest w porządku. Próbowałem również odejść od kroków encode i decode. Jeśli zostawiam je wyłączonym, narzeka, że ​​\u05d0 jest poza zakresem, próbując zdecydować, czy ta postać musi być cytowana w kodowaniu, które można wydrukować. Jeśli zostanę w kroku encode, z goryczą skarży się na to, jak przechodzę w numerze bytes i chce mieć str.

+0

Jeśli '„Moja wiadomość z «\ u05d0» w nim.”' Jest unicode chcecie, to nie można użyć '" Moja wiadomość z «\ u05d0» w it. ". encode ('utf-8') .odtoduj ('iso8859-1')', ponieważ jest to inny kod Unicode. (Zmienisz wiadomość.) – unutbu

+0

@unutbu: Gratulacje, że zauważyłeś, dlaczego kod jest bardzo brzydki. Ale działa. Osiąga pożądany rezultat. Zobacz moją aktualizację. – Omnifarious

Odpowiedz

8

Ten pakiet wiadomości e-mail nie jest mylony co do którego jest zakodowana (kodowane dane w formacie Unicode i dane binarne), ale dokumentacja nie czyni tego jasnym, ponieważ znaczna część dokumentacji pochodzi z epoki, w której "kodowanie "oznacza kodowanie transferu treści. Pracujemy nad lepszym interfejsem API, który ułatwi grokowanie (i lepsze dokumenty).

Istnieje sposób na to, aby pakiet e-mail używał QP do treści utf-8, ale nie jest on dobrze udokumentowany. Robisz to tak:

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

Dziękujemy! To doskonale odpowiada na moje pytanie i daje mi sposób na zrobienie tego, co chcę, to nie jest niepokojący hack. :-) – Omnifarious

+1

To dobrze poradzi sobie z Twoją postacią. Ale nie obsługuje postaci \ u05d0. W rzeczywistości nie koduje twojej postaci jako utf-8, koduje ją jako iso8859-1. : -/ – Omnifarious

+0

Zobacz także Python [issue1525919] (http://bugs.python.org/issue1525919#msg29229). – mmoya

1

Running

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

Daje tę wiadomość Traceback:

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

Od

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

to jest oczywiste, że unicode '\u05d0' jest charakter problemu. _QUOPRI_BODY_MAP jest zdefiniowana w quoprimime.py przez

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

DICT ten zawiera tylko kluczy range(256). Więc myślę, że masz rację; quoprimime.py nie można użyć do kodowania dowolnego kodu Unicode.

Jako obejście, można użyć (domyślnie) base64 pomijając

c.body_encoding = email.charset.QP 

Należy pamiętać, że latest version z quoprimime.py nie używa _QUOPRI_BODY_MAP w ogóle, więc przy użyciu najnowszych Python może naprawić problem.

+2

Podejrzewam, że tak nie będzie. Wydaje się, że problem nie polega na odpowiedniej konwersji na bajty utf-8 przed zastosowaniem kodowania w formacie quoted-printable. Metody 'as_string' i' __str__' w 'mail.message.Message' powinny być przestarzałe na korzyść metod zwracających bajty. Domyślam się, że cały pakiet wiadomości e-mail jest trochę zagubiony w kwestii różnicy między kodowaniem binarnym wykonanym na wiadomości e-mail i kodowaniem domniemanym przy użyciu określonego systemu kodowania znaków. Te dwa pojęcia są właściwie oddzielnymi koncepcjami, mimo że oba używają terminu "kodowanie". – Omnifarious