merge heads

This commit is contained in:
Benjamin Peterson 2011-03-15 14:45:20 -05:00
commit e97a5b2bda
18 changed files with 139 additions and 38 deletions

View file

@ -537,10 +537,11 @@ code, or when embedding the Python interpreter:
operations such as ``PyEval_ReleaseThread(tstate)``. It is not needed before operations such as ``PyEval_ReleaseThread(tstate)``. It is not needed before
calling :c:func:`PyEval_SaveThread` or :c:func:`PyEval_RestoreThread`. calling :c:func:`PyEval_SaveThread` or :c:func:`PyEval_RestoreThread`.
.. index:: single: Py_Initialize()
This is a no-op when called for a second time. This is a no-op when called for a second time.
.. versionchanged:: 3.2
This function cannot be called before :c:func:`Py_Initialize()` anymore.
.. index:: module: _thread .. index:: module: _thread
.. note:: .. note::

View file

@ -46,15 +46,16 @@ Here are the methods of the :class:`Message` class:
be generated or modified). be generated or modified).
Note that this method is provided as a convenience and may not always Note that this method is provided as a convenience and may not always
format the message the way you want. For example, by default it mangles format the message the way you want. For example, by default it does
lines that begin with ``From``. For more flexibility, instantiate a not do the mangling of lines that begin with ``From`` that is
required by the unix mbox format. For more flexibility, instantiate a
:class:`~email.generator.Generator` instance and use its :meth:`flatten` :class:`~email.generator.Generator` instance and use its :meth:`flatten`
method directly. For example:: method directly. For example::
from io import StringIO from io import StringIO
from email.generator import Generator from email.generator import Generator
fp = StringIO() fp = StringIO()
g = Generator(fp, mangle_from_=False, maxheaderlen=60) g = Generator(fp, mangle_from_=True, maxheaderlen=60)
g.flatten(msg) g.flatten(msg)
text = fp.getvalue() text = fp.getvalue()

View file

@ -34,6 +34,20 @@ Protocol) and :rfc:`1869` (SMTP Service Extensions).
For normal use, you should only require the initialization/connect, For normal use, you should only require the initialization/connect,
:meth:`sendmail`, and :meth:`quit` methods. An example is included below. :meth:`sendmail`, and :meth:`quit` methods. An example is included below.
The :class:`SMTP` class supports the :keyword:`with` statement. When used
like this, the SMTP ``QUIT`` command is issued automatically when the
:keyword:`with` statement exits. E.g.::
>>> from smtplib import SMTP
>>> with SMTP("domain.org") as smtp:
... smtp.noop()
...
(250, b'Ok')
>>>
.. versionadded:: 3.3
Support for the :keyword:`with` statement was added.
.. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout]) .. class:: SMTP_SSL(host='', port=0, local_hostname=None, keyfile=None, certfile=None[, timeout])

View file

@ -2698,3 +2698,7 @@ require changes to your code:
a new function, :func:`asyncore.handle_accepted`, was added to replace it. a new function, :func:`asyncore.handle_accepted`, was added to replace it.
(Contributed by Giampaolo Rodola in :issue:`6706`.) (Contributed by Giampaolo Rodola in :issue:`6706`.)
* Due to the new :term:`GIL` implementation, :c:func:`PyEval_InitThreads()`
cannot be called before :c:func:`Py_Initialize()` anymore.

View file

@ -263,7 +263,7 @@ class Charset:
Returns "quoted-printable" if self.body_encoding is QP. Returns "quoted-printable" if self.body_encoding is QP.
Returns "base64" if self.body_encoding is BASE64. Returns "base64" if self.body_encoding is BASE64.
Returns "7bit" otherwise. Returns conversion function otherwise.
""" """
assert self.body_encoding != SHORTEST assert self.body_encoding != SHORTEST
if self.body_encoding == QP: if self.body_encoding == QP:
@ -381,7 +381,10 @@ class Charset:
"""Body-encode a string by converting it first to bytes. """Body-encode a string by converting it first to bytes.
The type of encoding (base64 or quoted-printable) will be based on The type of encoding (base64 or quoted-printable) will be based on
self.body_encoding. self.body_encoding. If body_encoding is None, we assume the
output charset is a 7bit encoding, so re-encoding the decoded
string using the ascii codec produces the correct string version
of the content.
""" """
# 7bit/8bit encodings return the string unchanged (module conversions) # 7bit/8bit encodings return the string unchanged (module conversions)
if self.body_encoding is BASE64: if self.body_encoding is BASE64:
@ -391,4 +394,6 @@ class Charset:
elif self.body_encoding is QP: elif self.body_encoding is QP:
return email.quoprimime.body_encode(string) return email.quoprimime.body_encode(string)
else: else:
if isinstance(string, str):
string = string.encode(self.output_charset).decode('ascii')
return string return string

View file

@ -54,10 +54,13 @@ def encode_7or8bit(msg):
# There's no payload. For backwards compatibility we use 7bit # There's no payload. For backwards compatibility we use 7bit
msg['Content-Transfer-Encoding'] = '7bit' msg['Content-Transfer-Encoding'] = '7bit'
return return
# We play a trick to make this go fast. If encoding to ASCII succeeds, we # We play a trick to make this go fast. If encoding/decode to ASCII
# know the data must be 7bit, otherwise treat it as 8bit. # succeeds, we know the data must be 7bit, otherwise treat it as 8bit.
try: try:
orig.encode('ascii') if isinstance(orig, str):
orig.encode('ascii')
else:
orig.decode('ascii')
except UnicodeError: except UnicodeError:
# iso-2022-* is non-ASCII but still 7-bit # iso-2022-* is non-ASCII but still 7-bit
charset = msg.get_charset() charset = msg.get_charset()

View file

@ -157,8 +157,7 @@ class Message:
header. header.
This is a convenience method and may not generate the message exactly This is a convenience method and may not generate the message exactly
as you intend because by default it mangles lines that begin with as you intend. For more flexibility, use the flatten() method of a
"From ". For more flexibility, use the flatten() method of a
Generator instance. Generator instance.
""" """
from email.generator import Generator from email.generator import Generator
@ -242,7 +241,7 @@ class Message:
raise TypeError('Expected list, got %s' % type(self._payload)) raise TypeError('Expected list, got %s' % type(self._payload))
payload = self._payload payload = self._payload
cte = self.get('content-transfer-encoding', '').lower() cte = self.get('content-transfer-encoding', '').lower()
# payload can be bytes here, (I wonder if that is actually a bug?) # payload may be bytes here.
if isinstance(payload, str): if isinstance(payload, str):
if _has_surrogates(payload): if _has_surrogates(payload):
bpayload = payload.encode('ascii', 'surrogateescape') bpayload = payload.encode('ascii', 'surrogateescape')

View file

@ -3372,9 +3372,9 @@ class TestCharset(unittest.TestCase):
# built-in encodings where the header encoding is QP but the body # built-in encodings where the header encoding is QP but the body
# encoding is not. # encoding is not.
from email import charset as CharsetModule from email import charset as CharsetModule
CharsetModule.add_charset('fake', CharsetModule.QP, None) CharsetModule.add_charset('fake', CharsetModule.QP, None, 'utf-8')
c = Charset('fake') c = Charset('fake')
eq('hello w\xf6rld', c.body_encode('hello w\xf6rld')) eq('hello world', c.body_encode('hello world'))
def test_unicode_charset_name(self): def test_unicode_charset_name(self):
charset = Charset('us-ascii') charset = Charset('us-ascii')

View file

@ -13,7 +13,7 @@ from email.message import Message
# We're compatible with Python 2.3, but it doesn't have the built-in Asian # We're compatible with Python 2.3, but it doesn't have the built-in Asian
# codecs, so we have to skip all these tests. # codecs, so we have to skip all these tests.
try: try:
str('foo', 'euc-jp') str(b'foo', 'euc-jp')
except LookupError: except LookupError:
raise unittest.SkipTest raise unittest.SkipTest
@ -22,11 +22,14 @@ except LookupError:
class TestEmailAsianCodecs(TestEmailBase): class TestEmailAsianCodecs(TestEmailBase):
def test_japanese_codecs(self): def test_japanese_codecs(self):
eq = self.ndiffAssertEqual eq = self.ndiffAssertEqual
j = Charset("euc-jp") jcode = "euc-jp"
g = Charset("iso-8859-1") gcode = "iso-8859-1"
j = Charset(jcode)
g = Charset(gcode)
h = Header("Hello World!") h = Header("Hello World!")
jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa' jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
ghello = 'Gr\xfc\xdf Gott!' b'\xa5\xeb\xa5\xc9\xa1\xaa', jcode)
ghello = str(b'Gr\xfc\xdf Gott!', gcode)
h.append(jhello, j) h.append(jhello, j)
h.append(ghello, g) h.append(ghello, g)
# BAW: This used to -- and maybe should -- fold the two iso-8859-1 # BAW: This used to -- and maybe should -- fold the two iso-8859-1
@ -36,13 +39,17 @@ class TestEmailAsianCodecs(TestEmailBase):
# encoded word. # encoded word.
eq(h.encode(), """\ eq(h.encode(), """\
Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?= Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?=
=?iso-8859-1?q?Gr=FC=DF?= =?iso-8859-1?q?_Gott!?=""") =?iso-8859-1?q?Gr=FC=DF_Gott!?=""")
eq(decode_header(h.encode()), eq(decode_header(h.encode()),
[('Hello World!', None), [(b'Hello World!', None),
('\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'), (b'\x1b$B%O%m!<%o!<%k%I!*\x1b(B', 'iso-2022-jp'),
('Gr\xfc\xdf Gott!', 'iso-8859-1')]) (b'Gr\xfc\xdf Gott!', gcode)])
int = 'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9' subject_bytes = (b'test-ja \xa4\xd8\xc5\xea\xb9\xc6\xa4\xb5'
h = Header(int, j, header_name="Subject") b'\xa4\xec\xa4\xbf\xa5\xe1\xa1\xbc\xa5\xeb\xa4\xcf\xbb\xca\xb2'
b'\xf1\xbc\xd4\xa4\xce\xbe\xb5\xc7\xa7\xa4\xf2\xc2\xd4\xa4\xc3'
b'\xa4\xc6\xa4\xa4\xa4\xde\xa4\xb9')
subject = str(subject_bytes, jcode)
h = Header(subject, j, header_name="Subject")
# test a very long header # test a very long header
enc = h.encode() enc = h.encode()
# TK: splitting point may differ by codec design and/or Header encoding # TK: splitting point may differ by codec design and/or Header encoding
@ -50,15 +57,24 @@ Hello World! =?iso-2022-jp?b?GyRCJU8lbSE8JW8hPCVrJUkhKhsoQg==?=
=?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?= =?iso-2022-jp?b?dGVzdC1qYSAbJEIkWEVqOUYkNSRsJD8lYSE8JWskTztKGyhC?=
=?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""") =?iso-2022-jp?b?GyRCMnE8VCROPjVHJyRyQlQkQyRGJCQkXiQ5GyhC?=""")
# TK: full decode comparison # TK: full decode comparison
eq(h.__unicode__().encode('euc-jp'), int) eq(str(h).encode(jcode), subject_bytes)
def test_payload_encoding_utf8(self):
jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
b'\xa5\xeb\xa5\xc9\xa1\xaa', 'euc-jp')
msg = Message()
msg.set_payload(jhello, 'utf-8')
ustr = msg.get_payload(decode=True).decode(msg.get_content_charset())
self.assertEqual(jhello, ustr)
def test_payload_encoding(self): def test_payload_encoding(self):
jhello = '\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc\xa5\xeb\xa5\xc9\xa1\xaa'
jcode = 'euc-jp' jcode = 'euc-jp'
jhello = str(b'\xa5\xcf\xa5\xed\xa1\xbc\xa5\xef\xa1\xbc'
b'\xa5\xeb\xa5\xc9\xa1\xaa', jcode)
msg = Message() msg = Message()
msg.set_payload(jhello, jcode) msg.set_payload(jhello, jcode)
ustr = str(msg.get_payload(), msg.get_content_charset()) ustr = msg.get_payload(decode=True).decode(msg.get_content_charset())
self.assertEqual(jhello, ustr.encode(jcode)) self.assertEqual(jhello, ustr)

View file

@ -269,6 +269,19 @@ class SMTP:
pass pass
self.local_hostname = '[%s]' % addr self.local_hostname = '[%s]' % addr
def __enter__(self):
return self
def __exit__(self, *args):
try:
code, message = self.docmd("QUIT")
if code != 221:
raise SMTPResponseException(code, message)
except SMTPServerDisconnected:
pass
finally:
self.close()
def set_debuglevel(self, debuglevel): def set_debuglevel(self, debuglevel):
"""Set the debug output level. """Set the debug output level.

View file

@ -3,10 +3,12 @@
# The specific tests now live in Lib/email/test # The specific tests now live in Lib/email/test
from email.test.test_email import suite from email.test.test_email import suite
from email.test.test_email_codecs import suite as codecs_suite
from test import support from test import support
def test_main(): def test_main():
support.run_unittest(suite()) support.run_unittest(suite())
support.run_unittest(codecs_suite())
if __name__ == '__main__': if __name__ == '__main__':
test_main() test_main()

View file

@ -424,6 +424,9 @@ sim_lists = {'list-1':['Mr.A@somewhere.com','Mrs.C@somewhereesle.com'],
# Simulated SMTP channel & server # Simulated SMTP channel & server
class SimSMTPChannel(smtpd.SMTPChannel): class SimSMTPChannel(smtpd.SMTPChannel):
# For testing failures in QUIT when using the context manager API.
quit_response = None
def __init__(self, extra_features, *args, **kw): def __init__(self, extra_features, *args, **kw):
self._extrafeatures = ''.join( self._extrafeatures = ''.join(
[ "250-{0}\r\n".format(x) for x in extra_features ]) [ "250-{0}\r\n".format(x) for x in extra_features ])
@ -475,19 +478,31 @@ class SimSMTPChannel(smtpd.SMTPChannel):
else: else:
self.push('550 No access for you!') self.push('550 No access for you!')
def smtp_QUIT(self, arg):
# args is ignored
if self.quit_response is None:
super(SimSMTPChannel, self).smtp_QUIT(arg)
else:
self.push(self.quit_response)
self.close_when_done()
def handle_error(self): def handle_error(self):
raise raise
class SimSMTPServer(smtpd.SMTPServer): class SimSMTPServer(smtpd.SMTPServer):
# For testing failures in QUIT when using the context manager API.
quit_response = None
def __init__(self, *args, **kw): def __init__(self, *args, **kw):
self._extra_features = [] self._extra_features = []
smtpd.SMTPServer.__init__(self, *args, **kw) smtpd.SMTPServer.__init__(self, *args, **kw)
def handle_accepted(self, conn, addr): def handle_accepted(self, conn, addr):
self._SMTPchannel = SimSMTPChannel(self._extra_features, self._SMTPchannel = SimSMTPChannel(
self, conn, addr) self._extra_features, self, conn, addr)
self._SMTPchannel.quit_response = self.quit_response
def process_message(self, peer, mailfrom, rcpttos, data): def process_message(self, peer, mailfrom, rcpttos, data):
pass pass
@ -620,6 +635,25 @@ class SMTPSimTests(unittest.TestCase):
self.assertIn(sim_auth_credentials['cram-md5'], str(err)) self.assertIn(sim_auth_credentials['cram-md5'], str(err))
smtp.close() smtp.close()
def test_with_statement(self):
with smtplib.SMTP(HOST, self.port) as smtp:
code, message = smtp.noop()
self.assertEqual(code, 250)
self.assertRaises(smtplib.SMTPServerDisconnected, smtp.send, b'foo')
with smtplib.SMTP(HOST, self.port) as smtp:
smtp.close()
self.assertRaises(smtplib.SMTPServerDisconnected, smtp.send, b'foo')
def test_with_statement_QUIT_failure(self):
self.serv.quit_response = '421 QUIT FAILED'
with self.assertRaises(smtplib.SMTPResponseException) as error:
with smtplib.SMTP(HOST, self.port) as smtp:
smtp.noop()
self.assertEqual(error.exception.smtp_code, 421)
self.assertEqual(error.exception.smtp_error, b'QUIT FAILED')
# We don't need to clean up self.serv.quit_response because a new
# server is always instantiated in the setUp().
#TODO: add tests for correct AUTH method fallback now that the #TODO: add tests for correct AUTH method fallback now that the
#test infrastructure can support it. #test infrastructure can support it.

View file

@ -376,6 +376,7 @@ Kevan Heydon
Jason Hildebrand Jason Hildebrand
Richie Hindle Richie Hindle
Konrad Hinsen Konrad Hinsen
Michael Henry
David Hobley David Hobley
Tim Hochberg Tim Hochberg
Joerg-Cyril Hoehle Joerg-Cyril Hoehle

View file

@ -68,6 +68,12 @@ Core and Builtins
Library Library
------- -------
- Issue #11289: `smtp.SMTP` class becomes a context manager so it can be used
in a `with` statement. Contributed by Giampaolo Rodola.
- Issue #11554: Fixed support for Japanese codecs; previously the body output
encoding was not done if euc-jp or shift-jis was specified as the charset.
- Issue #11509: Significantly increase test coverage of fileinput. - Issue #11509: Significantly increase test coverage of fileinput.
Patch by Denver Coneybeare at PyCon 2011 Sprints. Patch by Denver Coneybeare at PyCon 2011 Sprints.
@ -206,6 +212,8 @@ Tools/Demos
Tests Tests
----- -----
- Issue #11554: Reactivated test_email_codecs.
- Issue #11505: improves test coverage of string.py - Issue #11505: improves test coverage of string.py
- Issue #11490: test_subprocess:test_leaking_fds_on_error no longer gives a - Issue #11490: test_subprocess:test_leaking_fds_on_error no longer gives a

View file

@ -3084,7 +3084,7 @@
2003-09-18 Kaz Kojima <kkojima@gcc.gnu.org> 2003-09-18 Kaz Kojima <kkojima@gcc.gnu.org>
* src/sh/ffi.c (ffi_prep_args): Take account into the alignment * src/sh/ffi.c (ffi_prep_args): Take account into the alignement
for the register size. for the register size.
(ffi_closure_helper_SYSV): Handle the structure return value (ffi_closure_helper_SYSV): Handle the structure return value
address correctly. address correctly.
@ -3344,7 +3344,7 @@
2003-02-06 Andreas Tobler <a.tobler@schweiz.ch> 2003-02-06 Andreas Tobler <a.tobler@schweiz.ch>
* libffi/src/powerpc/darwin_closure.S: * libffi/src/powerpc/darwin_closure.S:
Fix alignment bug, allocate 8 bytes for the result. Fix alignement bug, allocate 8 bytes for the result.
* libffi/src/powerpc/aix_closure.S: * libffi/src/powerpc/aix_closure.S:
Likewise. Likewise.
* libffi/src/powerpc/ffi_darwin.c: * libffi/src/powerpc/ffi_darwin.c:

View file

@ -1326,7 +1326,7 @@ static void* win32direct_mmap(size_t size) {
return (ptr != 0)? ptr: MFAIL; return (ptr != 0)? ptr: MFAIL;
} }
/* This function supports releasing coalesced segments */ /* This function supports releasing coalesed segments */
static int win32munmap(void* ptr, size_t size) { static int win32munmap(void* ptr, size_t size) {
MEMORY_BASIC_INFORMATION minfo; MEMORY_BASIC_INFORMATION minfo;
char* cptr = ptr; char* cptr = ptr;
@ -1362,7 +1362,7 @@ static int win32munmap(void* ptr, size_t size) {
#define CALL_MORECORE(S) MFAIL #define CALL_MORECORE(S) MFAIL
#endif /* HAVE_MORECORE */ #endif /* HAVE_MORECORE */
/* mstate bit set if contiguous morecore disabled or failed */ /* mstate bit set if continguous morecore disabled or failed */
#define USE_NONCONTIGUOUS_BIT (4U) #define USE_NONCONTIGUOUS_BIT (4U)
/* segment bit set in create_mspace_with_base */ /* segment bit set in create_mspace_with_base */

View file

@ -592,7 +592,7 @@ typedef struct aix_fd_struct {
+---------------------------------------+ 160 +---------------------------------------+ 160
| result area 8 | | result area 8 |
+---------------------------------------+ 168 +---------------------------------------+ 168
| alignment to the next multiple of 16 | | alignement to the next multiple of 16 |
SP current --> +---------------------------------------+ 176 <- parent frame SP current --> +---------------------------------------+ 176 <- parent frame
| back chain to caller 4 | | back chain to caller 4 |
+---------------------------------------+ 180 +---------------------------------------+ 180

View file

@ -650,7 +650,7 @@ ffi_call(
+---------------------------------------+ 160 +---------------------------------------+ 160
| result area 8 | | result area 8 |
+---------------------------------------+ 168 +---------------------------------------+ 168
| alignment to the next multiple of 16 | | alignement to the next multiple of 16 |
SP current --> +---------------------------------------+ 176 <- parent frame SP current --> +---------------------------------------+ 176 <- parent frame
| back chain to caller 4 | | back chain to caller 4 |
+---------------------------------------+ 180 +---------------------------------------+ 180