[Python-checkins] r86330 - in python/branches/py3k-cdecimal: Doc/includes/email-mime.py Doc/includes/email-simple.py Doc/library/configparser.rst Doc/library/smtplib.rst Doc/library/stdtypes.rst Doc/library/xml.sax.handler.rst Doc/tutorial/introduction.rst Doc/whatsnew/3.2.rst Lib/smtplib.py Lib/test/regrtest.py Lib/test/test_smtplib.py Misc/NEWS Misc/RPM/README Objects/bytearrayobject.c Objects/bytesobject.c Objects/unicodeobject.c

stefan.krah python-checkins at python.org
Mon Nov 8 18:54:06 CET 2010


Author: stefan.krah
Date: Mon Nov  8 18:54:06 2010
New Revision: 86330

Log:
Merged revisions 86304-86305,86307-86310,86313,86315-86316,86324,86326-86327 via svnmerge from 
svn+ssh://pythondev@svn.python.org/python/branches/py3k

........
  r86304 | victor.stinner | 2010-11-07 19:41:46 +0100 (Sun, 07 Nov 2010) | 6 lines
  
  Fix encode/decode method doc of str, bytes, bytearray types
  
   * Specify the default encoding: write 'utf-8' instead of
     sys.getdefaultencoding(), because the default encoding is now constant
   * Specify the default errors value
........
  r86305 | victor.stinner | 2010-11-07 20:04:46 +0100 (Sun, 07 Nov 2010) | 2 lines
  
  str, bytes, bytearray docstring: remove unnecessary [...]
........
  r86307 | antoine.pitrou | 2010-11-07 21:50:51 +0100 (Sun, 07 Nov 2010) | 3 lines
  
  Issue #10347: ignore leading test count ("[  1/340]") when using the -f option to regrtest.
........
  r86308 | sean.reifschneider | 2010-11-07 22:26:53 +0100 (Sun, 07 Nov 2010) | 2 lines
  
  Removing reference to ftp.python.org and enhancing RPM README.
........
  r86309 | brian.curtin | 2010-11-07 23:09:05 +0100 (Sun, 07 Nov 2010) | 4 lines
  
  typo: annually->manually 
  
  ...unless these are commands you only run once a year :)
........
  r86310 | senthil.kumaran | 2010-11-08 02:53:13 +0100 (Mon, 08 Nov 2010) | 3 lines
  
  Fix Issue 10303: a small clarification in the tutorial.
........
  r86313 | senthil.kumaran | 2010-11-08 03:04:05 +0100 (Mon, 08 Nov 2010) | 3 lines
  
  Extra space caught by the post-commit-hook, aka Taggnostr :)
........
  r86315 | georg.brandl | 2010-11-08 12:05:18 +0100 (Mon, 08 Nov 2010) | 1 line
  
  Fix latex conversion glitch in property/feature descriptions.
........
  r86316 | georg.brandl | 2010-11-08 12:08:35 +0100 (Mon, 08 Nov 2010) | 1 line
  
  Fix typo.
........
  r86324 | georg.brandl | 2010-11-08 17:57:52 +0100 (Mon, 08 Nov 2010) | 1 line
  
  Fix next version name.
........
  r86326 | eric.araujo | 2010-11-08 18:13:03 +0100 (Mon, 08 Nov 2010) | 2 lines
  
  Move a news entry to the right section (+ light reformatting)
........
  r86327 | r.david.murray | 2010-11-08 18:15:13 +0100 (Mon, 08 Nov 2010) | 3 lines
  
  #10321: Add support for sending binary DATA and Message objects to smtplib
........


Modified:
   python/branches/py3k-cdecimal/   (props changed)
   python/branches/py3k-cdecimal/Doc/includes/email-mime.py
   python/branches/py3k-cdecimal/Doc/includes/email-simple.py
   python/branches/py3k-cdecimal/Doc/library/configparser.rst
   python/branches/py3k-cdecimal/Doc/library/smtplib.rst
   python/branches/py3k-cdecimal/Doc/library/stdtypes.rst
   python/branches/py3k-cdecimal/Doc/library/xml.sax.handler.rst
   python/branches/py3k-cdecimal/Doc/tutorial/introduction.rst
   python/branches/py3k-cdecimal/Doc/whatsnew/3.2.rst
   python/branches/py3k-cdecimal/Lib/smtplib.py
   python/branches/py3k-cdecimal/Lib/test/regrtest.py
   python/branches/py3k-cdecimal/Lib/test/test_smtplib.py
   python/branches/py3k-cdecimal/Misc/NEWS
   python/branches/py3k-cdecimal/Misc/RPM/README
   python/branches/py3k-cdecimal/Objects/bytearrayobject.c
   python/branches/py3k-cdecimal/Objects/bytesobject.c
   python/branches/py3k-cdecimal/Objects/unicodeobject.c

Modified: python/branches/py3k-cdecimal/Doc/includes/email-mime.py
==============================================================================
--- python/branches/py3k-cdecimal/Doc/includes/email-mime.py	(original)
+++ python/branches/py3k-cdecimal/Doc/includes/email-mime.py	Mon Nov  8 18:54:06 2010
@@ -27,5 +27,5 @@
 
 # Send the email via our own SMTP server.
 s = smtplib.SMTP()
-s.sendmail(me, family, msg.as_string())
+s.sendmail(msg)
 s.quit()

Modified: python/branches/py3k-cdecimal/Doc/includes/email-simple.py
==============================================================================
--- python/branches/py3k-cdecimal/Doc/includes/email-simple.py	(original)
+++ python/branches/py3k-cdecimal/Doc/includes/email-simple.py	Mon Nov  8 18:54:06 2010
@@ -17,8 +17,7 @@
 msg['From'] = me
 msg['To'] = you
 
-# Send the message via our own SMTP server, but don't include the
-# envelope header.
+# Send the message via our own SMTP server.
 s = smtplib.SMTP()
-s.sendmail(me, [you], msg.as_string())
+s.sendmail(msg)
 s.quit()

Modified: python/branches/py3k-cdecimal/Doc/library/configparser.rst
==============================================================================
--- python/branches/py3k-cdecimal/Doc/library/configparser.rst	(original)
+++ python/branches/py3k-cdecimal/Doc/library/configparser.rst	Mon Nov  8 18:54:06 2010
@@ -37,7 +37,7 @@
 
 Configuration files may include comments, prefixed by specific characters (``#``
 and ``;`` by default).  Comments may appear on their own in an otherwise empty
-line, or may be entered in lines holding values or spection names.  In the
+line, or may be entered in lines holding values or section names.  In the
 latter case, they need to be preceded by a whitespace character to be recognized
 as a comment.  (For backwards compatibility, by default only ``;`` starts an
 inline comment, while ``#`` does not.)

Modified: python/branches/py3k-cdecimal/Doc/library/smtplib.rst
==============================================================================
--- python/branches/py3k-cdecimal/Doc/library/smtplib.rst	(original)
+++ python/branches/py3k-cdecimal/Doc/library/smtplib.rst	Mon Nov  8 18:54:06 2010
@@ -274,9 +274,14 @@
    .. note::
 
       The *from_addr* and *to_addrs* parameters are used to construct the message
-      envelope used by the transport agents. The :class:`SMTP` does not modify the
+      envelope used by the transport agents.  ``sendmail`` does not modify the
       message headers in any way.
 
+   msg may be a string containing characters in the ASCII range, or a byte
+   string.  A string is encoded to bytes using the ascii codec, and lone ``\r``
+   and ``\n`` characters are converted to ``\r\n`` characters.  A byte string
+   is not modified.
+
    If there has been no previous ``EHLO`` or ``HELO`` command this session, this
    method tries ESMTP ``EHLO`` first. If the server does ESMTP, message size and
    each of the specified options will be passed to it (if the option is in the
@@ -311,6 +316,27 @@
    Unless otherwise noted, the connection will be open even after an exception is
    raised.
 
+   .. versionchanged:: 3.2 *msg* may be a byte string.
+
+
+.. method:: SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=[], rcpt_options=[])
+
+   This is a convenience method for calling :meth:`sendmail` with the message
+   represented by an :class:`email.message.Message` object.  The arguments have
+   the same meaning as for :meth:`sendmail`, except that *msg* is a ``Message``
+   object.
+
+   If *from_addr* is ``None``, ``send_message`` sets its value to the value of
+   the :mailheader:`From` header from *msg*.  If *to_addrs* is ``None``,
+   ``send_message`` combines the values (if any) of the :mailheader:`To`,
+   :mailheader:`CC`, and :mailheader:`Bcc` fields from *msg*.  Regardless of
+   the values of *from_addr* and *to_addrs*, ``send_message`` deletes any  Bcc
+   field from *msg*.  It then serializes *msg* using
+   :class:`~email.generator.BytesGenerator` with ``\r\n`` as the *linesep*, and
+   calls :meth:`sendmail` to transmit the resulting message.
+
+   .. versionadded:: 3.2
+
 
 .. method:: SMTP.quit()
 
@@ -366,5 +392,5 @@
 .. note::
 
    In general, you will want to use the :mod:`email` package's features to
-   construct an email message, which you can then convert to a string and send
-   via :meth:`sendmail`; see :ref:`email-examples`.
+   construct an email message, which you can then send
+   via :meth:`~smtplib.SMTP.send_message`; see :ref:`email-examples`.

Modified: python/branches/py3k-cdecimal/Doc/library/stdtypes.rst
==============================================================================
--- python/branches/py3k-cdecimal/Doc/library/stdtypes.rst	(original)
+++ python/branches/py3k-cdecimal/Doc/library/stdtypes.rst	Mon Nov  8 18:54:06 2010
@@ -992,12 +992,12 @@
    interpreted as in slice notation.
 
 
-.. method:: str.encode(encoding=sys.getdefaultencoding(), errors="strict")
+.. method:: str.encode(encoding="utf-8", errors="strict")
 
-   Return an encoded version of the string as a bytes object.  Default encoding
-   is the current default string encoding.  *errors* may be given to set a
-   different error handling scheme.  The default for *errors* is ``'strict'``,
-   meaning that encoding errors raise a :exc:`UnicodeError`.  Other possible
+   Return an encoded version of the string as a bytes object. Default encoding
+   is ``'utf-8'``. *errors* may be given to set a different error handling scheme.
+   The default for *errors* is ``'strict'``, meaning that encoding errors raise
+   a :exc:`UnicodeError`. Other possible
    values are ``'ignore'``, ``'replace'``, ``'xmlcharrefreplace'``,
    ``'backslashreplace'`` and any other name registered via
    :func:`codecs.register_error`, see section :ref:`codec-base-classes`. For a
@@ -1765,11 +1765,11 @@
       b = a.replace(b"a", b"f")
 
 
-.. method:: bytes.decode(encoding=sys.getdefaultencoding(), errors="strict")
-            bytearray.decode(encoding=sys.getdefaultencoding(), errors="strict")
+.. method:: bytes.decode(encoding="utf-8", errors="strict")
+            bytearray.decode(encoding="utf-8", errors="strict")
 
-   Return a string decoded from the given bytes.  Default encoding is the
-   current default string encoding.  *errors* may be given to set a different
+   Return a string decoded from the given bytes.  Default encoding is
+   ``'utf-8'``. *errors* may be given to set a different
    error handling scheme.  The default for *errors* is ``'strict'``, meaning
    that encoding errors raise a :exc:`UnicodeError`.  Other possible values are
    ``'ignore'``, ``'replace'`` and any other name registered via

Modified: python/branches/py3k-cdecimal/Doc/library/xml.sax.handler.rst
==============================================================================
--- python/branches/py3k-cdecimal/Doc/library/xml.sax.handler.rst	(original)
+++ python/branches/py3k-cdecimal/Doc/library/xml.sax.handler.rst	Mon Nov  8 18:54:06 2010
@@ -49,52 +49,57 @@
 
 .. data:: feature_namespaces
 
-   Value: ``"http://xml.org/sax/features/namespaces"`` ---  true: Perform Namespace
-   processing. ---  false: Optionally do not perform Namespace processing (implies
-   namespace-prefixes; default). ---  access: (parsing) read-only; (not parsing)
-   read/write
+   | value: ``"http://xml.org/sax/features/namespaces"``
+   | true: Perform Namespace processing.
+   | false: Optionally do not perform Namespace processing (implies
+     namespace-prefixes; default).
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: feature_namespace_prefixes
 
-   Value: ``"http://xml.org/sax/features/namespace-prefixes"`` --- true: Report
-   the original prefixed names and attributes used for Namespace
-   declarations. --- false: Do not report attributes used for Namespace
-   declarations, and optionally do not report original prefixed names
-   (default). --- access: (parsing) read-only; (not parsing) read/write
+   | value: ``"http://xml.org/sax/features/namespace-prefixes"``
+   | true: Report the original prefixed names and attributes used for Namespace
+     declarations.
+   | false: Do not report attributes used for Namespace declarations, and
+     optionally do not report original prefixed names (default).
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: feature_string_interning
 
-   Value: ``"http://xml.org/sax/features/string-interning"`` ---  true: All element
-   names, prefixes, attribute names, Namespace URIs, and local names are interned
-   using the built-in intern function. ---  false: Names are not necessarily
-   interned, although they may be (default). ---  access: (parsing) read-only; (not
-   parsing) read/write
+   | value: ``"http://xml.org/sax/features/string-interning"``
+   | true: All element names, prefixes, attribute names, Namespace URIs, and
+     local names are interned using the built-in intern function.
+   | false: Names are not necessarily interned, although they may be (default).
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: feature_validation
 
-   Value: ``"http://xml.org/sax/features/validation"`` --- true: Report all
-   validation errors (implies external-general-entities and
-   external-parameter-entities). --- false: Do not report validation errors. ---
-   access: (parsing) read-only; (not parsing) read/write
+   | value: ``"http://xml.org/sax/features/validation"``
+   | true: Report all validation errors (implies external-general-entities and
+     external-parameter-entities).
+   | false: Do not report validation errors.
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: feature_external_ges
 
-   Value: ``"http://xml.org/sax/features/external-general-entities"`` ---  true:
-   Include all external general (text) entities. ---  false: Do not include
-   external general entities. ---  access: (parsing) read-only; (not parsing)
-   read/write
+   | value: ``"http://xml.org/sax/features/external-general-entities"``
+   | true: Include all external general (text) entities.
+   | false: Do not include external general entities.
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: feature_external_pes
 
-   Value: ``"http://xml.org/sax/features/external-parameter-entities"`` ---  true:
-   Include all external parameter entities, including the external DTD subset. ---
-   false: Do not include any external parameter entities, even the external DTD
-   subset. ---  access: (parsing) read-only; (not parsing) read/write
+   | value: ``"http://xml.org/sax/features/external-parameter-entities"``
+   | true: Include all external parameter entities, including the external DTD
+     subset.
+   | false: Do not include any external parameter entities, even the external
+     DTD subset.
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: all_features
@@ -104,34 +109,38 @@
 
 .. data:: property_lexical_handler
 
-   Value: ``"http://xml.org/sax/properties/lexical-handler"`` ---  data type:
-   xml.sax.sax2lib.LexicalHandler (not supported in Python 2) ---  description: An
-   optional extension handler for lexical events like comments. ---  access:
-   read/write
+   | value: ``"http://xml.org/sax/properties/lexical-handler"``
+   | data type: xml.sax.sax2lib.LexicalHandler (not supported in Python 2)
+   | description: An optional extension handler for lexical events like
+     comments.
+   | access: read/write
 
 
 .. data:: property_declaration_handler
 
-   Value: ``"http://xml.org/sax/properties/declaration-handler"`` ---  data type:
-   xml.sax.sax2lib.DeclHandler (not supported in Python 2) ---  description: An
-   optional extension handler for DTD-related events other than notations and
-   unparsed entities. ---  access: read/write
+   | value: ``"http://xml.org/sax/properties/declaration-handler"``
+   | data type: xml.sax.sax2lib.DeclHandler (not supported in Python 2)
+   | description: An optional extension handler for DTD-related events other
+     than notations and unparsed entities.
+   | access: read/write
 
 
 .. data:: property_dom_node
 
-   Value: ``"http://xml.org/sax/properties/dom-node"`` ---  data type:
-   org.w3c.dom.Node (not supported in Python 2)  ---  description: When parsing,
-   the current DOM node being visited if this is a DOM iterator; when not parsing,
-   the root DOM node for iteration. ---  access: (parsing) read-only; (not parsing)
-   read/write
+   | value: ``"http://xml.org/sax/properties/dom-node"``
+   | data type: org.w3c.dom.Node (not supported in Python 2)
+   | description: When parsing, the current DOM node being visited if this is
+     a DOM iterator; when not parsing, the root DOM node for iteration.
+   | access: (parsing) read-only; (not parsing) read/write
 
 
 .. data:: property_xml_string
 
-   Value: ``"http://xml.org/sax/properties/xml-string"`` ---  data type: String ---
-   description: The literal string of characters that was the source for the
-   current event. ---  access: read-only
+   | value: ``"http://xml.org/sax/properties/xml-string"``
+   | data type: String
+   | description: The literal string of characters that was the source for the
+     current event.
+   | access: read-only
 
 
 .. data:: all_properties

Modified: python/branches/py3k-cdecimal/Doc/tutorial/introduction.rst
==============================================================================
--- python/branches/py3k-cdecimal/Doc/tutorial/introduction.rst	(original)
+++ python/branches/py3k-cdecimal/Doc/tutorial/introduction.rst	Mon Nov  8 18:54:06 2010
@@ -194,8 +194,8 @@
 are typed for input: inside quotes, and with quotes and other funny characters
 escaped by backslashes, to show the precise value.  The string is enclosed in
 double quotes if the string contains a single quote and no double quotes, else
-it's enclosed in single quotes.  Once again, the :func:`print` function
-produces the more readable output.
+it's enclosed in single quotes.  The :func:`print` function produces a more
+readable output for such input strings.
 
 String literals can span multiple lines in several ways.  Continuation lines can
 be used, with a backslash as the last character on the line indicating that the

Modified: python/branches/py3k-cdecimal/Doc/whatsnew/3.2.rst
==============================================================================
--- python/branches/py3k-cdecimal/Doc/whatsnew/3.2.rst	(original)
+++ python/branches/py3k-cdecimal/Doc/whatsnew/3.2.rst	Mon Nov  8 18:54:06 2010
@@ -540,6 +540,14 @@
 
   (Contributed by Neil Schemenauer and Nick Coghlan; :issue:`5178`.)
 
+* The :mod:`smtplib` :class:`~smtplib.SMTP` class now accepts a byte string
+  for the *msg* argument to the :meth:`~smtplib.SMTP.sendmail` method,
+  and a new method, :meth:`~smtplib.SMTP.send_message` accepts a
+  :class:`~email.message.Message` object and can optionally obtain the
+  *from_addr* and *to_addrs* addresses directly from the object.
+
+  (Contributed by R. David Murray, :issue:`10321`.)
+
 
 Multi-threading
 ===============

Modified: python/branches/py3k-cdecimal/Lib/smtplib.py
==============================================================================
--- python/branches/py3k-cdecimal/Lib/smtplib.py	(original)
+++ python/branches/py3k-cdecimal/Lib/smtplib.py	Mon Nov  8 18:54:06 2010
@@ -42,8 +42,11 @@
 # This was modified from the Python 1.5 library HTTP lib.
 
 import socket
+import io
 import re
 import email.utils
+import email.message
+import email.generator
 import base64
 import hmac
 from email.base64mime import body_encode as encode_base64
@@ -57,6 +60,7 @@
 SMTP_PORT = 25
 SMTP_SSL_PORT = 465
 CRLF="\r\n"
+bCRLF=b"\r\n"
 
 OLDSTYLE_AUTH = re.compile(r"auth=(.*)", re.I)
 
@@ -147,6 +151,7 @@
     else:
         return "<%s>" % m
 
+# Legacy method kept for backward compatibility.
 def quotedata(data):
     """Quote data for email.
 
@@ -156,6 +161,12 @@
     return re.sub(r'(?m)^\.', '..',
         re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data))
 
+def _quote_periods(bindata):
+    return re.sub(br'(?m)^\.', '..', bindata)
+
+def _fix_eols(data):
+    return  re.sub(r'(?:\r\n|\n|\r(?!\n))', CRLF, data)
+
 try:
     import ssl
 except ImportError:
@@ -469,7 +480,9 @@
         Automatically quotes lines beginning with a period per rfc821.
         Raises SMTPDataError if there is an unexpected reply to the
         DATA command; the return value from this method is the final
-        response code received when the all data is sent.
+        response code received when the all data is sent.  If msg
+        is a string, lone '\r' and '\n' characters are converted to
+        '\r\n' characters.  If msg is bytes, it is transmitted as is.
         """
         self.putcmd("data")
         (code,repl)=self.getreply()
@@ -477,10 +490,12 @@
         if code != 354:
             raise SMTPDataError(code,repl)
         else:
-            q = quotedata(msg)
-            if q[-2:] != CRLF:
-                q = q + CRLF
-            q = q + "." + CRLF
+            if isinstance(msg, str):
+                msg = _fix_eols(msg).encode('ascii')
+            q = _quote_periods(msg)
+            if q[-2:] != bCRLF:
+                q = q + bCRLF
+            q = q + b"." + bCRLF
             self.send(q)
             (code,msg)=self.getreply()
             if self.debuglevel >0 : print("data:", (code,msg), file=stderr)
@@ -648,6 +663,10 @@
             - rcpt_options : List of ESMTP options (such as DSN commands) for
                              all the rcpt commands.
 
+        msg may be a string containing characters in the ASCII range, or a byte
+        string.  A string is encoded to bytes using the ascii codec, and lone
+        \r and \n characters are converted to \r\n characters.
+
         If there has been no previous EHLO or HELO command this session, this
         method tries ESMTP EHLO first.  If the server does ESMTP, message size
         and each of the specified options will be passed to it.  If EHLO
@@ -693,6 +712,8 @@
         """
         self.ehlo_or_helo_if_needed()
         esmtp_opts = []
+        if isinstance(msg, str):
+            msg = _fix_eols(msg).encode('ascii')
         if self.does_esmtp:
             # Hmmm? what's this? -ddm
             # self.esmtp_features['7bit']=""
@@ -700,7 +721,6 @@
                 esmtp_opts.append("size=%d" % len(msg))
             for option in mail_options:
                 esmtp_opts.append(option)
-
         (code,resp) = self.mail(from_addr, esmtp_opts)
         if code != 250:
             self.rset()
@@ -723,6 +743,33 @@
         #if we got here then somebody got our mail
         return senderrs
 
+    def send_message(self, msg, from_addr=None, to_addrs=None,
+                mail_options=[], rcpt_options={}):
+        """Converts message to a bytestring and passes it to sendmail.
+
+        The arguments are as for sendmail, except that msg is an
+        email.message.Message object.  If from_addr is None, the from_addr is
+        taken from the 'From' header of the Message.  If to_addrs is None, its
+        value is composed from the addresses listed in the 'To', 'CC', and
+        'Bcc' fields.  Regardless of the values of from_addr and to_addr, any
+        Bcc field in the Message object is deleted.  The Message object is then
+        serialized using email.generator.BytesGenerator and sendmail is called
+        to transmit the message.
+        """
+        if from_addr is None:
+            from_addr = msg['From']
+        if to_addrs is None:
+            addr_fields = [f for f in (msg['To'], msg['Bcc'], msg['CC'])
+                            if f is not None]
+            to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
+        del msg['Bcc']
+        with io.BytesIO() as bytesmsg:
+            g = email.generator.BytesGenerator(bytesmsg)
+            g.flatten(msg, linesep='\r\n')
+            flatmsg = bytesmsg.getvalue()
+        return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
+                             rcpt_options)
+
 
     def close(self):
         """Close the connection to the SMTP server."""

Modified: python/branches/py3k-cdecimal/Lib/test/regrtest.py
==============================================================================
--- python/branches/py3k-cdecimal/Lib/test/regrtest.py	(original)
+++ python/branches/py3k-cdecimal/Lib/test/regrtest.py	Mon Nov  8 18:54:06 2010
@@ -424,7 +424,9 @@
     if fromfile:
         tests = []
         fp = open(os.path.join(support.SAVEDCWD, fromfile))
+        count_pat = re.compile(r'\[\s*\d+/\s*\d+\]')
         for line in fp:
+            line = count_pat.sub('', line)
             guts = line.split() # assuming no test has whitespace in its name
             if guts and not guts[0].startswith('#'):
                 tests.extend(guts)

Modified: python/branches/py3k-cdecimal/Lib/test/test_smtplib.py
==============================================================================
--- python/branches/py3k-cdecimal/Lib/test/test_smtplib.py	(original)
+++ python/branches/py3k-cdecimal/Lib/test/test_smtplib.py	Mon Nov  8 18:54:06 2010
@@ -1,9 +1,11 @@
 import asyncore
+import email.mime.text
 import email.utils
 import socket
 import smtpd
 import smtplib
 import io
+import re
 import sys
 import time
 import select
@@ -57,6 +59,13 @@
     def tearDown(self):
         smtplib.socket = socket
 
+    # This method is no longer used but is retained for backward compatibility,
+    # so test to make sure it still works.
+    def testQuoteData(self):
+        teststr  = "abc\n.jkl\rfoo\r\n..blue"
+        expected = "abc\r\n..jkl\r\nfoo\r\n...blue"
+        self.assertEqual(expected, smtplib.quotedata(teststr))
+
     def testBasic1(self):
         mock_socket.reply_with(b"220 Hola mundo")
         # connects
@@ -150,6 +159,8 @@
 @unittest.skipUnless(threading, 'Threading required for this test.')
 class DebuggingServerTests(unittest.TestCase):
 
+    maxDiff = None
+
     def setUp(self):
         self.real_getfqdn = socket.getfqdn
         socket.getfqdn = mock_socket.getfqdn
@@ -161,6 +172,9 @@
         self._threads = support.threading_setup()
         self.serv_evt = threading.Event()
         self.client_evt = threading.Event()
+        # Capture SMTPChannel debug output
+        self.old_DEBUGSTREAM = smtpd.DEBUGSTREAM
+        smtpd.DEBUGSTREAM = io.StringIO()
         # Pick a random unused port by passing 0 for the port number
         self.serv = smtpd.DebuggingServer((HOST, 0), ('nowhere', -1))
         # Keep a note of what port was assigned
@@ -183,6 +197,9 @@
         support.threading_cleanup(*self._threads)
         # restore sys.stdout
         sys.stdout = self.old_stdout
+        # restore DEBUGSTREAM
+        smtpd.DEBUGSTREAM.close()
+        smtpd.DEBUGSTREAM = self.old_DEBUGSTREAM
 
     def testBasic(self):
         # connect
@@ -247,6 +264,95 @@
         mexpect = '%s%s\n%s' % (MSG_BEGIN, m, MSG_END)
         self.assertEqual(self.output.getvalue(), mexpect)
 
+    def testSendBinary(self):
+        m = b'A test message'
+        smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
+        smtp.sendmail('John', 'Sally', m)
+        # XXX (see comment in testSend)
+        time.sleep(0.01)
+        smtp.quit()
+
+        self.client_evt.set()
+        self.serv_evt.wait()
+        self.output.flush()
+        mexpect = '%s%s\n%s' % (MSG_BEGIN, m.decode('ascii'), MSG_END)
+        self.assertEqual(self.output.getvalue(), mexpect)
+
+    def testSendMessage(self):
+        m = email.mime.text.MIMEText('A test message')
+        smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
+        smtp.send_message(m, from_addr='John', to_addrs='Sally')
+        # XXX (see comment in testSend)
+        time.sleep(0.01)
+        smtp.quit()
+
+        self.client_evt.set()
+        self.serv_evt.wait()
+        self.output.flush()
+        # Add the X-Peer header that DebuggingServer adds
+        # XXX: I'm not sure hardcoding this IP will work on linux-vserver.
+        m['X-Peer'] = '127.0.0.1'
+        mexpect = '%s%s\n%s' % (MSG_BEGIN, m.as_string(), MSG_END)
+        self.assertEqual(self.output.getvalue(), mexpect)
+
+    def testSendMessageWithAddresses(self):
+        m = email.mime.text.MIMEText('A test message')
+        m['From'] = 'foo at bar.com'
+        m['To'] = 'John'
+        m['CC'] = 'Sally, Fred'
+        m['Bcc'] = 'John Root <root at localhost>, "Dinsdale" <warped at silly.walks.com>'
+        smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
+        smtp.send_message(m)
+        # XXX (see comment in testSend)
+        time.sleep(0.01)
+        smtp.quit()
+
+        self.client_evt.set()
+        self.serv_evt.wait()
+        self.output.flush()
+        # Add the X-Peer header that DebuggingServer adds
+        # XXX: I'm not sure hardcoding this IP will work on linux-vserver.
+        m['X-Peer'] = '127.0.0.1'
+        # The Bcc header is deleted before serialization.
+        del m['Bcc']
+        mexpect = '%s%s\n%s' % (MSG_BEGIN, m.as_string(), MSG_END)
+        self.assertEqual(self.output.getvalue(), mexpect)
+        debugout = smtpd.DEBUGSTREAM.getvalue()
+        sender = re.compile("^sender: foo at bar.com$", re.MULTILINE)
+        self.assertRegexpMatches(debugout, sender)
+        for addr in ('John', 'Sally', 'Fred', 'root at localhost',
+                     'warped at silly.walks.com'):
+            to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
+                                 re.MULTILINE)
+            self.assertRegexpMatches(debugout, to_addr)
+
+    def testSendMessageWithSomeAddresses(self):
+        # Make sure nothing breaks if not all of the three 'to' headers exist
+        m = email.mime.text.MIMEText('A test message')
+        m['From'] = 'foo at bar.com'
+        m['To'] = 'John, Dinsdale'
+        smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
+        smtp.send_message(m)
+        # XXX (see comment in testSend)
+        time.sleep(0.01)
+        smtp.quit()
+
+        self.client_evt.set()
+        self.serv_evt.wait()
+        self.output.flush()
+        # Add the X-Peer header that DebuggingServer adds
+        # XXX: I'm not sure hardcoding this IP will work on linux-vserver.
+        m['X-Peer'] = '127.0.0.1'
+        mexpect = '%s%s\n%s' % (MSG_BEGIN, m.as_string(), MSG_END)
+        self.assertEqual(self.output.getvalue(), mexpect)
+        debugout = smtpd.DEBUGSTREAM.getvalue()
+        sender = re.compile("^sender: foo at bar.com$", re.MULTILINE)
+        self.assertRegexpMatches(debugout, sender)
+        for addr in ('John', 'Dinsdale'):
+            to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
+                                 re.MULTILINE)
+            self.assertRegexpMatches(debugout, to_addr)
+
 
 class NonConnectingTests(unittest.TestCase):
 

Modified: python/branches/py3k-cdecimal/Misc/NEWS
==============================================================================
--- python/branches/py3k-cdecimal/Misc/NEWS	(original)
+++ python/branches/py3k-cdecimal/Misc/NEWS	Mon Nov  8 18:54:06 2010
@@ -2,8 +2,8 @@
 Python News
 +++++++++++
 
-What's New in Python 3.2 Beta 1?
-================================
+What's New in Python 3.2 Alpha 4?
+=================================
 
 *Release date: XX-Nov-2010*
 
@@ -27,11 +27,6 @@
 - Issue #10186: Fix the SyntaxError caret when the offset is equal to the length
   of the offending line.
 
-- Issue #6011: sysconfig and distutils.sysconfig use the surrogateescape error
-  handler to parse the Makefile file. Avoid a UnicodeDecodeError if the source
-  code directory name contains a non-ASCII character and the locale encoding is
-  ASCII.
-
 - Issue #10089: Add support for arbitrary -X options on the command-line.
   They can be retrieved through a new attribute ``sys._xoptions``.
 
@@ -65,8 +60,16 @@
 Library
 -------
 
+- Issue #10321: Added support for binary data to smtplib.SMTP.sendmail,
+  and a new method send_message to send an email.message.Message object.
+
+- Issue #6011: sysconfig and distutils.sysconfig use the surrogateescape error
+  handler to parse the Makefile file.  Avoid a UnicodeDecodeError if the source
+  code directory name contains a non-ASCII character and the locale encoding is
+  ASCII.
+
 - Issue #10329: The trace module writes reports using the input Python script
-  encoding, instead of the locale encoding. Patch written by Alexander
+  encoding, instead of the locale encoding.  Patch written by Alexander
   Belopolsky.
 
 - Issue #10126: Fix distutils' test_build when Python was built with

Modified: python/branches/py3k-cdecimal/Misc/RPM/README
==============================================================================
--- python/branches/py3k-cdecimal/Misc/RPM/README	(original)
+++ python/branches/py3k-cdecimal/Misc/RPM/README	Mon Nov  8 18:54:06 2010
@@ -2,11 +2,6 @@
 Python.  Its contents are maintained by Sean Reifschneider
 <jafo at tummy.com>.
 
-It is recommended that RPM builders use the python*.src.rpm file
-downloaded from the "ftp.python.org:/pub/python/<version>/rpms".  These
-may be more up to date than the files included in the base Python
-release tar-file.
-
 If you wish to build RPMs from the base Python release tar-file, note
 that you will have to download the
 "doc/<version>/html-<version>.tar.bz2"
@@ -14,3 +9,25 @@
 the build to complete.  This is the same directory that you place the
 Python-2.3.1 release tar-file in.  You can then use the ".spec" file in
 this directory to build RPMs.
+
+You may also wish to pursue RPMs provided by distribution makers to see if
+they have one suitable for your uses.  If, for example, you just want a
+slightly newer version of Python than what the distro provides, you could
+pick up the closest SRPM your distro provides, and then modify it to
+the newer version, and build that.  It may be as simple as just changing
+the "version" information in the spec file (or it may require fixing
+patches).
+
+NOTE: I am *NOT* recommending just using the binary RPM, and never do an
+install with "--force" or "--nodeps".
+
+Also worth pursuing may be newer versions provided by similar distros.  For
+example, a Python 3 SRPM from Fedora may be a good baseline to try building
+on CentOS.
+
+Many newer SRPMs won't install on older distros because of format changes.
+You can manually extract these SRPMS with:
+
+   mkdir foo
+   cd foo
+   rpm2cpio <../python3-*.src.rpm | cpio -ivd

Modified: python/branches/py3k-cdecimal/Objects/bytearrayobject.c
==============================================================================
--- python/branches/py3k-cdecimal/Objects/bytearrayobject.c	(original)
+++ python/branches/py3k-cdecimal/Objects/bytearrayobject.c	Mon Nov  8 18:54:06 2010
@@ -2465,10 +2465,10 @@
 }
 
 PyDoc_STRVAR(decode_doc,
-"B.decode([encoding[, errors]]) -> str\n\
+"B.decode(encoding='utf-8', errors='strict') -> str\n\
 \n\
-Decode B using the codec registered for encoding. encoding defaults\n\
-to the default encoding. errors may be given to set a different error\n\
+Decode B using the codec registered for encoding. Default encoding\n\
+is 'utf-8'. errors may be given to set a different error\n\
 handling scheme.  Default is 'strict' meaning that encoding errors raise\n\
 a UnicodeDecodeError.  Other possible values are 'ignore' and 'replace'\n\
 as well as any other name registered with codecs.register_error that is\n\

Modified: python/branches/py3k-cdecimal/Objects/bytesobject.c
==============================================================================
--- python/branches/py3k-cdecimal/Objects/bytesobject.c	(original)
+++ python/branches/py3k-cdecimal/Objects/bytesobject.c	Mon Nov  8 18:54:06 2010
@@ -2289,10 +2289,10 @@
 
 
 PyDoc_STRVAR(decode__doc__,
-"B.decode([encoding[, errors]]) -> str\n\
+"B.decode(encoding='utf-8', errors='strict') -> str\n\
 \n\
-Decode B using the codec registered for encoding. encoding defaults\n\
-to the default encoding. errors may be given to set a different error\n\
+Decode B using the codec registered for encoding. Default encoding\n\
+is 'utf-8'. errors may be given to set a different error\n\
 handling scheme.  Default is 'strict' meaning that encoding errors raise\n\
 a UnicodeDecodeError.  Other possible values are 'ignore' and 'replace'\n\
 as well as any other name registerd with codecs.register_error that is\n\

Modified: python/branches/py3k-cdecimal/Objects/unicodeobject.c
==============================================================================
--- python/branches/py3k-cdecimal/Objects/unicodeobject.c	(original)
+++ python/branches/py3k-cdecimal/Objects/unicodeobject.c	Mon Nov  8 18:54:06 2010
@@ -7393,10 +7393,10 @@
 }
 
 PyDoc_STRVAR(encode__doc__,
-             "S.encode([encoding[, errors]]) -> bytes\n\
+             "S.encode(encoding='utf-8', errors='strict') -> bytes\n\
 \n\
-Encode S using the codec registered for encoding. encoding defaults\n\
-to the default encoding. errors may be given to set a different error\n\
+Encode S using the codec registered for encoding. Default encoding\n\
+is 'utf-8'. errors may be given to set a different error\n\
 handling scheme. Default is 'strict' meaning that encoding errors raise\n\
 a UnicodeEncodeError. Other possible values are 'ignore', 'replace' and\n\
 'xmlcharrefreplace' as well as any other name registered with\n\


More information about the Python-checkins mailing list