Как я могу использовать почтовый модуль Python 3.2 для отправки юникодных сообщений, закодированных в utf-8, с цитируемой печатью?
Я хочу отправлять сообщения электронной почты с произвольным телом Unicode в программе Python 3.2. Но на самом деле эти сообщения будут состоять в основном из 7-битного текста ASCII. Поэтому я хотел бы, чтобы сообщения, закодированные в utf-8, использовали 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)
Это приводит к сообщению электронной почты с абсолютно правильным содержанием:
To: someone@example.com
From: someone_else@example.com
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.
Особенно b'\xd7\x90'.decode('utf-8')
приводит к исходному символу Unicode. Итак quoted-printable
кодирование правильно рендеринг utf-8
, Я прекрасно понимаю, что это невероятно безобразный хак. Но это работает.
Это Python 3. Предполагается, что текстовые строки всегда будут в Юникоде. Мне не нужно было декодировать его до utf-8. А потом превратив его из bytes
Вернуться в str
от .decode('iso8859-1')
это ужасный хак, и я не должен был этого делать.
Это email
модуль просто сломан относительно кодировок? Я что-то не понимаю?
Я попытался просто установить старый, без набора символов. Это оставляет меня с сообщением электронной почты в юникоде, и это совсем не правильно. Я также пытался оставить encode
а также decode
шаги. Если я оставлю их обоих, он жалуется, что \u05d0
находится вне допустимого диапазона при попытке решить, должен ли этот символ быть заключен в кавычки для печати в кавычках. Если я уйду только encode
шаг, он горько жалуется на то, как я прохожу в bytes
и он хочет str
,
2 ответа
Этот пакет электронной почты не смущен тем, что есть что (кодированный юникод в сравнении с двоичными данными, закодированными при передаче контента), но документация не очень ясно дает понять, так как большая часть документации датируется эпохой, когда "кодирование" означало контент- Transfer-Encoding. Мы работаем над улучшением API, который облегчит все это (и улучшит документацию).
На самом деле есть способ заставить пакет электронной почты использовать QP для тел utf-8, но это не очень хорошо задокументировано. Вы делаете это так:
>>> 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'
Бег
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())
Получает это сообщение трассировки:
File "/usr/lib/python3.2/email/quoprimime.py", line 81, in body_check
return chr(octet) != _QUOPRI_BODY_MAP[octet]
KeyError: 1488
поскольку
In [11]: int('5d0',16)
Out[11]: 1488
понятно что юникод '\u05d0'
это проблема характера. _QUOPRI_BODY_MAP
определяется в quoprimime.py
_QUOPRI_HEADER_MAP = dict((c, '=%02X' % c) for c in range(256))
_QUOPRI_BODY_MAP = _QUOPRI_HEADER_MAP.copy()
Этот дикт содержит только ключи от range(256)
, Поэтому я думаю, что вы правы; quoprimime.py
не может использоваться для кодирования произвольного Unicode.
В качестве обходного пути вы можете использовать (по умолчанию) base64, опуская
c.body_encoding = email.charset.QP
Обратите внимание, что последняя версия quoprimime.py не использует _QUOPRI_BODY_MAP
В общем, использование последней версии Python может решить проблему.