Merge email package 4.0 from the sandbox, including documentation, test cases,

and NEWS updates.
This commit is contained in:
Barry Warsaw 2006-03-18 15:41:53 +00:00
parent 9ae019bf5b
commit 40ef0067ad
44 changed files with 3821 additions and 468 deletions

View file

@ -39,9 +39,6 @@ NL = '\n'
EMPTYSTRING = ''
SPACE = ' '
# We don't care about DeprecationWarnings
warnings.filterwarnings('ignore', '', DeprecationWarning, __name__)
def openfile(filename, mode='r'):
@ -87,7 +84,7 @@ class TestMessageAPI(TestEmailBase):
charset = Charset('iso-8859-1')
msg.set_charset(charset)
eq(msg['mime-version'], '1.0')
eq(msg.get_type(), 'text/plain')
eq(msg.get_content_type(), 'text/plain')
eq(msg['content-type'], 'text/plain; charset="iso-8859-1"')
eq(msg.get_param('charset'), 'iso-8859-1')
eq(msg['content-transfer-encoding'], 'quoted-printable')
@ -211,6 +208,19 @@ class TestMessageAPI(TestEmailBase):
msg.set_payload('foo')
eq(msg.get_payload(decode=True), 'foo')
def test_decode_bogus_uu_payload_quietly(self):
msg = Message()
msg.set_payload('begin 664 foo.txt\n%<W1F=0000H \n \nend\n')
msg['Content-Transfer-Encoding'] = 'x-uuencode'
old_stderr = sys.stderr
try:
sys.stderr = sfp = StringIO()
# We don't care about the payload
msg.get_payload(decode=True)
finally:
sys.stderr = old_stderr
self.assertEqual(sfp.getvalue(), '')
def test_decoded_generator(self):
eq = self.assertEqual
msg = self._msgobj('msg_07.txt')
@ -893,7 +903,7 @@ class TestMIMEAudio(unittest.TestCase):
self._au = MIMEAudio(self._audiodata)
def test_guess_minor_type(self):
self.assertEqual(self._au.get_type(), 'audio/basic')
self.assertEqual(self._au.get_content_type(), 'audio/basic')
def test_encoding(self):
payload = self._au.get_payload()
@ -901,7 +911,7 @@ class TestMIMEAudio(unittest.TestCase):
def test_checkSetMinor(self):
au = MIMEAudio(self._audiodata, 'fish')
self.assertEqual(au.get_type(), 'audio/fish')
self.assertEqual(au.get_content_type(), 'audio/fish')
def test_add_header(self):
eq = self.assertEqual
@ -936,7 +946,7 @@ class TestMIMEImage(unittest.TestCase):
self._im = MIMEImage(self._imgdata)
def test_guess_minor_type(self):
self.assertEqual(self._im.get_type(), 'image/gif')
self.assertEqual(self._im.get_content_type(), 'image/gif')
def test_encoding(self):
payload = self._im.get_payload()
@ -944,7 +954,7 @@ class TestMIMEImage(unittest.TestCase):
def test_checkSetMinor(self):
im = MIMEImage(self._imgdata, 'fish')
self.assertEqual(im.get_type(), 'image/fish')
self.assertEqual(im.get_content_type(), 'image/fish')
def test_add_header(self):
eq = self.assertEqual
@ -976,7 +986,7 @@ class TestMIMEText(unittest.TestCase):
def test_types(self):
eq = self.assertEqual
unless = self.failUnless
eq(self._msg.get_type(), 'text/plain')
eq(self._msg.get_content_type(), 'text/plain')
eq(self._msg.get_param('charset'), 'us-ascii')
missing = []
unless(self._msg.get_param('foobar', missing) is missing)
@ -1045,7 +1055,7 @@ This is the dingus fish.
# tests
m = self._msg
unless(m.is_multipart())
eq(m.get_type(), 'multipart/mixed')
eq(m.get_content_type(), 'multipart/mixed')
eq(len(m.get_payload()), 2)
raises(IndexError, m.get_payload, 2)
m0 = m.get_payload(0)
@ -1379,7 +1389,7 @@ class TestNonConformant(TestEmailBase):
def test_parse_missing_minor_type(self):
eq = self.assertEqual
msg = self._msgobj('msg_14.txt')
eq(msg.get_type(), 'text')
eq(msg.get_content_type(), 'text/plain')
eq(msg.get_content_maintype(), 'text')
eq(msg.get_content_subtype(), 'plain')
@ -1531,7 +1541,7 @@ class TestMIMEMessage(TestEmailBase):
m = Message()
m['Subject'] = subject
r = MIMEMessage(m)
eq(r.get_type(), 'message/rfc822')
eq(r.get_content_type(), 'message/rfc822')
payload = r.get_payload()
unless(isinstance(payload, list))
eq(len(payload), 1)
@ -1572,7 +1582,7 @@ Here is the body of the message.
eq = self.assertEqual
unless = self.failUnless
msg = self._msgobj('msg_11.txt')
eq(msg.get_type(), 'message/rfc822')
eq(msg.get_content_type(), 'message/rfc822')
payload = msg.get_payload()
unless(isinstance(payload, list))
eq(len(payload), 1)
@ -1586,12 +1596,12 @@ Here is the body of the message.
unless = self.failUnless
# msg 16 is a Delivery Status Notification, see RFC 1894
msg = self._msgobj('msg_16.txt')
eq(msg.get_type(), 'multipart/report')
eq(msg.get_content_type(), 'multipart/report')
unless(msg.is_multipart())
eq(len(msg.get_payload()), 3)
# Subpart 1 is a text/plain, human readable section
subpart = msg.get_payload(0)
eq(subpart.get_type(), 'text/plain')
eq(subpart.get_content_type(), 'text/plain')
eq(subpart.get_payload(), """\
This report relates to a message you sent with the following header fields:
@ -1611,7 +1621,7 @@ Your message cannot be delivered to the following recipients:
# consists of two blocks of headers, represented by two nested Message
# objects.
subpart = msg.get_payload(1)
eq(subpart.get_type(), 'message/delivery-status')
eq(subpart.get_content_type(), 'message/delivery-status')
eq(len(subpart.get_payload()), 2)
# message/delivery-status should treat each block as a bunch of
# headers, i.e. a bunch of Message objects.
@ -1629,13 +1639,13 @@ Your message cannot be delivered to the following recipients:
eq(dsn2.get_param('rfc822', header='final-recipient'), '')
# Subpart 3 is the original message
subpart = msg.get_payload(2)
eq(subpart.get_type(), 'message/rfc822')
eq(subpart.get_content_type(), 'message/rfc822')
payload = subpart.get_payload()
unless(isinstance(payload, list))
eq(len(payload), 1)
subsubpart = payload[0]
unless(isinstance(subsubpart, Message))
eq(subsubpart.get_type(), 'text/plain')
eq(subsubpart.get_content_type(), 'text/plain')
eq(subsubpart['message-id'],
'<002001c144a6$8752e060$56104586@oxy.edu>')
@ -1706,16 +1716,16 @@ Two
fp.close()
container1 = msg.get_payload(0)
eq(container1.get_default_type(), 'message/rfc822')
eq(container1.get_type(), None)
eq(container1.get_content_type(), 'message/rfc822')
container2 = msg.get_payload(1)
eq(container2.get_default_type(), 'message/rfc822')
eq(container2.get_type(), None)
eq(container2.get_content_type(), 'message/rfc822')
container1a = container1.get_payload(0)
eq(container1a.get_default_type(), 'text/plain')
eq(container1a.get_type(), 'text/plain')
eq(container1a.get_content_type(), 'text/plain')
container2a = container2.get_payload(0)
eq(container2a.get_default_type(), 'text/plain')
eq(container2a.get_type(), 'text/plain')
eq(container2a.get_content_type(), 'text/plain')
def test_default_type_with_explicit_container_type(self):
eq = self.assertEqual
@ -1726,16 +1736,16 @@ Two
fp.close()
container1 = msg.get_payload(0)
eq(container1.get_default_type(), 'message/rfc822')
eq(container1.get_type(), 'message/rfc822')
eq(container1.get_content_type(), 'message/rfc822')
container2 = msg.get_payload(1)
eq(container2.get_default_type(), 'message/rfc822')
eq(container2.get_type(), 'message/rfc822')
eq(container2.get_content_type(), 'message/rfc822')
container1a = container1.get_payload(0)
eq(container1a.get_default_type(), 'text/plain')
eq(container1a.get_type(), 'text/plain')
eq(container1a.get_content_type(), 'text/plain')
container2a = container2.get_payload(0)
eq(container2a.get_default_type(), 'text/plain')
eq(container2a.get_type(), 'text/plain')
eq(container2a.get_content_type(), 'text/plain')
def test_default_type_non_parsed(self):
eq = self.assertEqual
@ -1750,9 +1760,9 @@ Two
subpart2 = MIMEMessage(subpart2a)
container.attach(subpart1)
container.attach(subpart2)
eq(subpart1.get_type(), 'message/rfc822')
eq(subpart1.get_content_type(), 'message/rfc822')
eq(subpart1.get_default_type(), 'message/rfc822')
eq(subpart2.get_type(), 'message/rfc822')
eq(subpart2.get_content_type(), 'message/rfc822')
eq(subpart2.get_default_type(), 'message/rfc822')
neq(container.as_string(0), '''\
Content-Type: multipart/digest; boundary="BOUNDARY"
@ -1784,9 +1794,9 @@ message 2
del subpart1['mime-version']
del subpart2['content-type']
del subpart2['mime-version']
eq(subpart1.get_type(), None)
eq(subpart1.get_content_type(), 'message/rfc822')
eq(subpart1.get_default_type(), 'message/rfc822')
eq(subpart2.get_type(), None)
eq(subpart2.get_content_type(), 'message/rfc822')
eq(subpart2.get_default_type(), 'message/rfc822')
neq(container.as_string(0), '''\
Content-Type: multipart/digest; boundary="BOUNDARY"
@ -1847,7 +1857,7 @@ class TestIdempotent(TestEmailBase):
def test_parse_text_message(self):
eq = self.assertEquals
msg, text = self._msgobj('msg_01.txt')
eq(msg.get_type(), 'text/plain')
eq(msg.get_content_type(), 'text/plain')
eq(msg.get_content_maintype(), 'text')
eq(msg.get_content_subtype(), 'plain')
eq(msg.get_params()[1], ('charset', 'us-ascii'))
@ -1859,7 +1869,7 @@ class TestIdempotent(TestEmailBase):
def test_parse_untyped_message(self):
eq = self.assertEquals
msg, text = self._msgobj('msg_03.txt')
eq(msg.get_type(), None)
eq(msg.get_content_type(), 'text/plain')
eq(msg.get_params(), None)
eq(msg.get_param('charset'), None)
self._idempotent(msg, text)
@ -1933,7 +1943,7 @@ class TestIdempotent(TestEmailBase):
unless = self.failUnless
# Get a message object and reset the seek pointer for other tests
msg, text = self._msgobj('msg_05.txt')
eq(msg.get_type(), 'multipart/report')
eq(msg.get_content_type(), 'multipart/report')
# Test the Content-Type: parameters
params = {}
for pk, pv in msg.get_params():
@ -1945,13 +1955,13 @@ class TestIdempotent(TestEmailBase):
eq(len(msg.get_payload()), 3)
# Make sure the subparts are what we expect
msg1 = msg.get_payload(0)
eq(msg1.get_type(), 'text/plain')
eq(msg1.get_content_type(), 'text/plain')
eq(msg1.get_payload(), 'Yadda yadda yadda\n')
msg2 = msg.get_payload(1)
eq(msg2.get_type(), None)
eq(msg2.get_content_type(), 'text/plain')
eq(msg2.get_payload(), 'Yadda yadda yadda\n')
msg3 = msg.get_payload(2)
eq(msg3.get_type(), 'message/rfc822')
eq(msg3.get_content_type(), 'message/rfc822')
self.failUnless(isinstance(msg3, Message))
payload = msg3.get_payload()
unless(isinstance(payload, list))
@ -1965,7 +1975,7 @@ class TestIdempotent(TestEmailBase):
unless = self.failUnless
msg, text = self._msgobj('msg_06.txt')
# Check some of the outer headers
eq(msg.get_type(), 'message/rfc822')
eq(msg.get_content_type(), 'message/rfc822')
# Make sure the payload is a list of exactly one sub-Message, and that
# that submessage has a type of text/plain
payload = msg.get_payload()
@ -1973,7 +1983,7 @@ class TestIdempotent(TestEmailBase):
eq(len(payload), 1)
msg1 = payload[0]
self.failUnless(isinstance(msg1, Message))
eq(msg1.get_type(), 'text/plain')
eq(msg1.get_content_type(), 'text/plain')
self.failUnless(isinstance(msg1.get_payload(), str))
eq(msg1.get_payload(), '\n')
@ -2058,13 +2068,19 @@ class TestMiscellaneous(TestEmailBase):
module = __import__('email')
all = module.__all__
all.sort()
self.assertEqual(all, ['Charset', 'Encoders', 'Errors', 'Generator',
'Header', 'Iterators', 'MIMEAudio', 'MIMEBase',
'MIMEImage', 'MIMEMessage', 'MIMEMultipart',
'MIMENonMultipart', 'MIMEText', 'Message',
'Parser', 'Utils', 'base64MIME',
'message_from_file', 'message_from_string',
'quopriMIME'])
self.assertEqual(all, [
# Old names
'Charset', 'Encoders', 'Errors', 'Generator',
'Header', 'Iterators', 'MIMEAudio', 'MIMEBase',
'MIMEImage', 'MIMEMessage', 'MIMEMultipart',
'MIMENonMultipart', 'MIMEText', 'Message',
'Parser', 'Utils', 'base64MIME',
# new names
'base64mime', 'charset', 'encoders', 'errors', 'generator',
'header', 'iterators', 'message', 'message_from_file',
'message_from_string', 'mime', 'parser',
'quopriMIME', 'quoprimime', 'utils',
])
def test_formatdate(self):
now = time.time()
@ -2356,7 +2372,7 @@ class TestParsers(TestEmailBase):
fp.close()
eq(msg['from'], 'ppp-request@zzz.org')
eq(msg['to'], 'ppp@zzz.org')
eq(msg.get_type(), 'multipart/mixed')
eq(msg.get_content_type(), 'multipart/mixed')
self.failIf(msg.is_multipart())
self.failUnless(isinstance(msg.get_payload(), str))
@ -2405,10 +2421,10 @@ Here's the message body
fp.close()
eq(len(msg.get_payload()), 2)
part1 = msg.get_payload(0)
eq(part1.get_type(), 'text/plain')
eq(part1.get_content_type(), 'text/plain')
eq(part1.get_payload(), 'Simple email with attachment.\r\n\r\n')
part2 = msg.get_payload(1)
eq(part2.get_type(), 'application/riscos')
eq(part2.get_content_type(), 'application/riscos')
def test_multipart_digest_with_extra_mime_headers(self):
eq = self.assertEqual
@ -2427,21 +2443,21 @@ Here's the message body
eq(msg.is_multipart(), 1)
eq(len(msg.get_payload()), 2)
part1 = msg.get_payload(0)
eq(part1.get_type(), 'message/rfc822')
eq(part1.get_content_type(), 'message/rfc822')
eq(part1.is_multipart(), 1)
eq(len(part1.get_payload()), 1)
part1a = part1.get_payload(0)
eq(part1a.is_multipart(), 0)
eq(part1a.get_type(), 'text/plain')
eq(part1a.get_content_type(), 'text/plain')
neq(part1a.get_payload(), 'message 1\n')
# next message/rfc822
part2 = msg.get_payload(1)
eq(part2.get_type(), 'message/rfc822')
eq(part2.get_content_type(), 'message/rfc822')
eq(part2.is_multipart(), 1)
eq(len(part2.get_payload()), 1)
part2a = part2.get_payload(0)
eq(part2a.is_multipart(), 0)
eq(part2a.get_type(), 'text/plain')
eq(part2a.get_content_type(), 'text/plain')
neq(part2a.get_payload(), 'message 2\n')
def test_three_lines(self):
@ -2723,6 +2739,11 @@ class TestCharset(unittest.TestCase):
c = Charset('fake')
eq('hello w\xf6rld', c.body_encode('hello w\xf6rld'))
def test_unicode_charset_name(self):
charset = Charset(u'us-ascii')
self.assertEqual(str(charset), 'us-ascii')
self.assertRaises(Errors.CharsetError, Charset, 'asc\xffii')
# Test multilingual MIME headers.