Как я могу использовать почтовый модуль Python 3.2 для отправки юникодных сообщений, закодированных в utf-8, с цитируемым принтером? - PullRequest
7 голосов
/ 23 февраля 2012

Я хочу отправлять сообщения электронной почты с произвольным телом 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 просто сломан по отношению к кодировкам? Я что-то не понимаю?

Я пытался просто установить старый набор без набора символов. Это оставляет меня с почтовым сообщением Unicode, и это совсем не правильно. Я также попытался опустить шаги encode и decode. Если я оставлю их обоих выключенными, они будут жаловаться на то, что \u05d0 выходит за пределы допустимого диапазона при попытке решить, нужно ли указывать этот символ в кодировке для печати в кавычках. Если я ухожу только на шаге encode, он горько жалуется на то, как я передаю bytes, и он хочет str.

Ответы [ 2 ]

8 голосов
/ 01 марта 2012

Этот пакет электронной почты не смущается о том, что есть (в кодировке Unicode вместо двоичных данных, закодированных с передачей контента), но документация не очень понятна, так как большая часть документации датируется эпохой, когда «кодирование» означает Content-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'
1 голос
/ 23 февраля 2012

Запуск

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 может решить проблему.

...