Matthew Frazier committed f8f542a

added support for arbitrary text encodings

Comments (0)

Files changed (2)


 charset.add_charset('utf-8', charset.QP, charset.QP, 'utf-8')
-def build_mime(msg):
+def build_mime(msg, encoding='utf-8'):
     Builds a `email.MIMEBase` instance out of a `~cartero.message.Message`.
     The basic pattern goes like this:
     - Then, if the message has attachments, `build_attachments` is used to
       wrap them in a multipart/mixed after the content.
     - Finally, the appropriate headers (To, From, Subject, Message-ID, etc.)
-      are added to the result.
+      are added to the result with `add_headers`.
+    :param msg: A `cartero.message.Message`.
+    :param encoding: The encoding to use for text content if ASCII doesn't
+                     work. For safety, it's best to use utf-8 or utf-16.
-    mime = build_content(msg)
+    mime = build_content(msg, encoding)
     if msg.attachments:
-        mime = build_attachments(msg, mime)
+        mime = build_attachments(msg, mime, encoding)
+    add_headers(msg, mime)
+    return mime
+def add_headers(msg, mime):
     mime['To'] = ', '.join(unicode(a) for a in
     mime['From'] = unicode(msg.sender)
     mime['Subject'] = msg.subject
     mime['Message-ID'] = make_msgid(hex(id(msg)))
     for key, value in msg.headers.items():
         mime[key] = value
-    return mime
-def build_content(msg):
+def build_content(msg, encoding):
     if msg.is_multipart:
-        return build_alternatives(msg)
+        return build_alternatives(msg, encoding)
     elif msg.html:
-        return mime_text(msg.html, 'html')
+        return mime_text(msg.html, 'html', encoding)
-        return mime_text(msg.text, 'plain')
+        return mime_text(msg.text, 'plain', encoding)
-def build_alternatives(msg):
-    multipart = MIMEMultipart(_subtype='alternative', encoding='utf-8')
-    multipart.attach(mime_text(msg.text))
-    multipart.attach(mime_text(msg.html, 'html'))
+def build_alternatives(msg, encoding):
+    multipart = MIMEMultipart(_subtype='alternative', encoding=encoding)
+    multipart.attach(mime_text(msg.text, 'plain', encoding))
+    multipart.attach(mime_text(msg.html, 'html', encoding))
     return multipart
-def build_attachments(msg, content_mime):
+def build_attachments(msg, content_mime, encoding):
     mime = MIMEMultipart(_subtype='mixed')
     for attachment in msg.attachments:
-        mime.attach(build_attachment(attachment))
+        mime.attach(build_attachment(attachment, encoding))
     return mime
-def build_attachment(attach):
+def build_attachment(attach, text_encoding='utf-8'):
     main, sub = attach.mimetype.split('/')
     if main == 'text':
         assert isinstance(, unicode)
-        mime = mime_text(, sub)
+        mime = mime_text(, sub, text_encoding)
         mime = MIMEBase(main, sub)
     return mime
-def mime_text(text, subtype='plain'):
-    string, encoding = mime_encoding(text)
+def mime_text(text, subtype='plain', encoding='utf-8'):
+    string, encoding = mime_encoding(text, encoding)
     return MIMEText(string, subtype, encoding)


     return msgid
-def mime_encoding(text):
+def mime_encoding(text, alternate='utf-8'):
     This takes the `unicode` string and first converts it to ASCII, and if
-    that fails instead uses UTF-8. It doesn't handle text that can't be
-    converted to UTF-8, because if you have text that can't be represented in
-    UTF-8 you have serious problems.
+    that fails instead uses the given alternate encoding. If the alternate
+    encoding can't be used, a `UnicodeError` will be raised as normal.
     :param text: The text to encode, as `unicode`.
+    :param alternate: The name of the alternate encoding to use, defaulting
+                      to ``utf-8``.
     :returns: A tuple, the encoded string as `str` and the encoding used to
               represent it.
         return text.encode('ascii'), 'ascii'
     except UnicodeEncodeError:
-        return text.encode('utf-8'), 'utf-8'
+        return text.encode(alternate), alternate