diff options
Diffstat (limited to 'Lib/email/Charset.py')
-rw-r--r-- | Lib/email/Charset.py | 92 |
1 files changed, 55 insertions, 37 deletions
diff --git a/Lib/email/Charset.py b/Lib/email/Charset.py index c0fe9d7..2050eb1 100644 --- a/Lib/email/Charset.py +++ b/Lib/email/Charset.py @@ -1,26 +1,27 @@ # Copyright (C) 2001,2002 Python Software Foundation -# Author: che@debian.org (Ben Gertzfield) - -try: - unicode -except NameError: - def _is_unicode(x): - return 1==0 -else: - # Use UnicodeType instead of built-in unicode for Py2.1 compatibility - from types import UnicodeType - def _is_unicode(x): - return isinstance(x, UnicodeType) +# Author: che@debian.org (Ben Gertzfield), barry@zope.com (Barry Warsaw) +from types import UnicodeType from email.Encoders import encode_7or8bit import email.base64MIME import email.quopriMIME +def _isunicode(s): + return isinstance(s, UnicodeType) + +# Python 2.2.1 and beyond has these symbols +try: + True, False +except NameError: + True = 1 + False = 0 + # Flags for types of header encodings -QP = 1 # Quoted-Printable -BASE64 = 2 # Base64 +QP = 1 # Quoted-Printable +BASE64 = 2 # Base64 +SHORTEST = 3 # the shorter of QP and base64, but only for headers # In "=?charset?q?hello_world?=", the =?, ?q?, and ?= add up to 7 MISC_LEN = 7 @@ -41,7 +42,7 @@ CHARSETS = { 'shift_jis': (BASE64, None, 'iso-2022-jp'), 'iso-2022-jp': (BASE64, None, None), 'koi8-r': (BASE64, BASE64, None), - 'utf-8': (BASE64, BASE64, 'utf-8'), + 'utf-8': (SHORTEST, BASE64, 'utf-8'), } # Aliases for other commonly-used names for character sets. Map @@ -90,9 +91,11 @@ def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): character set. Optional header_enc and body_enc is either Charset.QP for - quoted-printable, Charset.BASE64 for base64 encoding, or None for no - encoding. It describes how message headers and message bodies in the - input charset are to be encoded. Default is no encoding. + quoted-printable, Charset.BASE64 for base64 encoding, Charset.SHORTEST for + the shortest of qp or base64 encoding, or None for no encoding. SHORTEST + is only valid for header_enc. It describes how message headers and + message bodies in the input charset are to be encoded. Default is no + encoding. Optional output_charset is the character set that the output should be in. Conversions will proceed from input charset, to Unicode, to the @@ -104,6 +107,8 @@ def add_charset(charset, header_enc=None, body_enc=None, output_charset=None): to add codecs the module does not know about. See the codec module's documentation for more information. """ + if body_enc == SHORTEST: + raise ValueError, 'SHORTEST not allowed for body_enc' CHARSETS[charset] = (header_enc, body_enc, output_charset) @@ -147,12 +152,14 @@ class Charset: header_encoding: If the character set must be encoded before it can be used in an email header, this attribute will be set to - Charset.QP (for quoted-printable) or Charset.BASE64 (for - base64 encoding). Otherwise, it will be None. + Charset.QP (for quoted-printable), Charset.BASE64 (for + base64 encoding), or Charset.SHORTEST for the shortest of + QP or BASE64 encoding. Otherwise, it will be None. body_encoding: Same as header_encoding, but describes the encoding for the mail message's body, which indeed may be different than the - header encoding. + header encoding. Charset.SHORTEST is not allowed for + body_encoding. output_charset: Some character sets must be converted before the can be used in email headers or bodies. If the input_charset is @@ -175,7 +182,7 @@ class Charset: # charset_map dictionary. Try that first, but let the user override # it. henc, benc, conv = CHARSETS.get(self.input_charset, - (BASE64, BASE64, None)) + (SHORTEST, SHORTEST, None)) # Set the attributes, allowing the arguments to override the default. self.header_encoding = henc self.body_encoding = benc @@ -209,6 +216,7 @@ class Charset: Returns "base64" if self.body_encoding is BASE64. Returns "7bit" otherwise. """ + assert self.body_encoding <> SHORTEST if self.body_encoding == QP: return 'quoted-printable' elif self.body_encoding == BASE64: @@ -236,7 +244,7 @@ class Charset: Characters that could not be converted to Unicode will be replaced with the Unicode replacement character U+FFFD. """ - if _is_unicode(s) or self.input_codec is None: + if _isunicode(s) or self.input_codec is None: return s try: return unicode(s, self.input_codec, 'replace') @@ -245,7 +253,7 @@ class Charset: # string unchanged. return s - def from_splittable(self, ustr, to_output=1): + def from_splittable(self, ustr, to_output=True): """Convert a splittable string back into an encoded string. Uses the proper codec to try and convert the string from @@ -256,15 +264,14 @@ class Charset: Characters that could not be converted from Unicode will be replaced with an appropriate character (usually '?'). - If to_output is true, uses output_codec to convert to an encoded - format. If to_output is false, uses input_codec. to_output defaults - to 1. + If to_output is True (the default), uses output_codec to convert to an + encoded format. If to_output is False, uses input_codec. """ if to_output: codec = self.output_codec else: codec = self.input_codec - if not _is_unicode(ustr) or codec is None: + if not _isunicode(ustr) or codec is None: return ustr try: return ustr.encode(codec, 'replace') @@ -284,22 +291,26 @@ class Charset: """Return the length of the encoded header string.""" cset = self.get_output_charset() # The len(s) of a 7bit encoding is len(s) - if self.header_encoding is BASE64: + if self.header_encoding == BASE64: return email.base64MIME.base64_len(s) + len(cset) + MISC_LEN - elif self.header_encoding is QP: + elif self.header_encoding == QP: return email.quopriMIME.header_quopri_len(s) + len(cset) + MISC_LEN + elif self.header_encoding == SHORTEST: + lenb64 = email.base64MIME.base64_len(s) + lenqp = email.quopriMIME.header_quopri_len(s) + return min(lenb64, lenqp) + len(cset) + MISC_LEN else: return len(s) - def header_encode(self, s, convert=0): + def header_encode(self, s, convert=False): """Header-encode a string, optionally converting it to output_charset. - If convert is true, the string will be converted from the input + If convert is True, the string will be converted from the input charset to the output charset automatically. This is not useful for multibyte character sets, which have line length issues (multibyte characters must be split on a character, not a byte boundary); use the high-level Header class to deal with these issues. convert defaults - to 0. + to False. The type of encoding (base64 or quoted-printable) will be based on self.header_encoding. @@ -308,17 +319,24 @@ class Charset: if convert: s = self.convert(s) # 7bit/8bit encodings return the string unchanged (modulo conversions) - if self.header_encoding is BASE64: + if self.header_encoding == BASE64: return email.base64MIME.header_encode(s, cset) - elif self.header_encoding is QP: + elif self.header_encoding == QP: return email.quopriMIME.header_encode(s, cset) + elif self.header_encoding == SHORTEST: + lenb64 = email.base64MIME.base64_len(s) + lenqp = email.quopriMIME.header_quopri_len(s) + if lenb64 < lenqp: + return email.base64MIME.header_encode(s, cset) + else: + return email.quopriMIME.header_encode(s, cset) else: return s - def body_encode(self, s, convert=1): + def body_encode(self, s, convert=True): """Body-encode a string and convert it to output_charset. - If convert is true (the default), the string will be converted from + If convert is True (the default), the string will be converted from the input charset to output charset automatically. Unlike header_encode(), there are no issues with byte boundaries and multibyte charsets in email bodies, so this is usually pretty safe. |