Big email 3.0 API changes, with updated unit tests and documentation.
Briefly (from the NEWS file):

- Updates for the email package:
  + All deprecated APIs that in email 2.x issued warnings have been removed:
    _encoder argument to the MIMEText constructor, Message.add_payload(),
    Utils.dump_address_pair(), Utils.decode(), Utils.encode()
  + New deprecations: Generator.__call__(), Message.get_type(),
    Message.get_main_type(), Message.get_subtype(), the 'strict' argument to
    the Parser constructor.  These will be removed in email 3.1.
  + Support for Python earlier than 2.3 has been removed (see PEP 291).
  + All defect classes have been renamed to end in 'Defect'.
  + Some FeedParser fixes; also a MultipartInvariantViolationDefect will be
    added to messages that claim to be multipart but really aren't.
  + Updates to documentation.
diff --git a/Lib/email/FeedParser.py b/Lib/email/FeedParser.py
index dc3027d..de2754e 100644
--- a/Lib/email/FeedParser.py
+++ b/Lib/email/FeedParser.py
@@ -1,5 +1,6 @@
 # Copyright (C) 2004 Python Software Foundation
 # Authors: Baxter, Wouters and Warsaw
+# Contact: email-sig@python.org
 
 """FeedParser - An email feed parser.
 
@@ -15,7 +16,7 @@
 The other advantage of this parser is that it will never throw a parsing
 exception.  Instead, when it finds something unexpected, it adds a 'defect' to
 the current message.  Defects are just instances that live on the message
-object's .defect attribute.
+object's .defects attribute.
 """
 
 import re
@@ -100,7 +101,7 @@
         # and the eol character(s).  Gather up a list of lines after
         # re-attaching the newlines.
         lines = []
-        for i in range(len(parts) / 2):
+        for i in range(len(parts) // 2):
             lines.append(parts[i*2] + parts[i*2+1])
         self.pushlines(lines)
 
@@ -156,6 +157,10 @@
         self._call_parse()
         root = self._pop_message()
         assert not self._msgstack
+        # Look for final set of defects
+        if root.get_content_maintype() == 'multipart' \
+               and not root.is_multipart():
+            root.defects.append(Errors.MultipartInvariantViolationDefect())
         return root
 
     def _new_message(self):
@@ -166,7 +171,6 @@
             self._msgstack[-1].attach(msg)
         self._msgstack.append(msg)
         self._cur = msg
-        self._cur.defects = []
         self._last = msg
 
     def _pop_message(self):
@@ -259,7 +263,7 @@
                 # defined a boundary.  That's a problem which we'll handle by
                 # reading everything until the EOF and marking the message as
                 # defective.
-                self._cur.defects.append(Errors.NoBoundaryInMultipart())
+                self._cur.defects.append(Errors.NoBoundaryInMultipartDefect())
                 lines = []
                 for line in self._input:
                     if line is NeedMoreData:
@@ -305,6 +309,8 @@
                             if eolmo:
                                 preamble[-1] = lastline[:-len(eolmo.group(0))]
                             self._cur.preamble = EMPTYSTRING.join(preamble)
+                        #import pdb ; pdb.set_trace()
+                        # See SF bug #1030941
                         capturing_preamble = False
                         self._input.unreadline(line)
                         continue
@@ -363,7 +369,7 @@
             # that as a defect and store the captured text as the payload.
             # Otherwise everything from here to the EOF is epilogue.
             if capturing_preamble:
-                self._cur.defects.append(Errors.StartBoundaryNotFound())
+                self._cur.defects.append(Errors.StartBoundaryNotFoundDefect())
                 self._cur.set_payload(EMPTYSTRING.join(preamble))
                 return
             # If the end boundary ended in a newline, we'll need to make sure
@@ -408,7 +414,7 @@
                     # The first line of the headers was a continuation.  This
                     # is illegal, so let's note the defect, store the illegal
                     # line, and ignore it for purposes of headers.
-                    defect = Errors.FirstHeaderLineIsContinuation(line)
+                    defect = Errors.FirstHeaderLineIsContinuationDefect(line)
                     self._cur.defects.append(defect)
                     continue
                 lastvalue.append(line)
@@ -436,13 +442,13 @@
                 else:
                     # Weirdly placed unix-from line.  Note this as a defect
                     # and ignore it.
-                    defect = Errors.MisplacedEnvelopeHeader(line)
+                    defect = Errors.MisplacedEnvelopeHeaderDefect(line)
                     self._cur.defects.append(defect)
                     continue
             # Split the line on the colon separating field name from value.
             i = line.find(':')
             if i < 0:
-                defect = Errors.MalformedHeader(line)
+                defect = Errors.MalformedHeaderDefect(line)
                 self._cur.defects.append(defect)
                 continue
             lastheader = line[:i]