merge heads

This commit is contained in:
Benjamin Peterson 2011-07-03 13:45:33 -05:00
commit 92843e3c14
15 changed files with 440 additions and 145 deletions

View File

@ -348,21 +348,32 @@ An :class:`SMTP` instance has the following methods:
.. versionchanged:: 3.2 *msg* may be a byte string. .. versionchanged:: 3.2 *msg* may be a byte string.
.. method:: SMTP.send_message(msg, from_addr=None, to_addrs=None, mail_options=[], rcpt_options=[]) .. 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 This is a convenience method for calling :meth:`sendmail` with the message
represented by an :class:`email.message.Message` object. The arguments have represented by an :class:`email.message.Message` object. The arguments have
the same meaning as for :meth:`sendmail`, except that *msg* is a ``Message`` the same meaning as for :meth:`sendmail`, except that *msg* is a ``Message``
object. object.
If *from_addr* is ``None``, ``send_message`` sets its value to the value of If *from_addr* is ``None`` or *to_addrs* is ``None``, ``send_message`` fills
the :mailheader:`From` header from *msg*. If *to_addrs* is ``None``, those arguments with addresses extracted from the headers of *msg* as
``send_message`` combines the values (if any) of the :mailheader:`To`, specified in :rfc:`2822`\: *from_addr* is set to the :mailheader:`Sender`
:mailheader:`CC`, and :mailheader:`Bcc` fields from *msg*. Regardless of field if it is present, and otherwise to the :mailheader:`From` field.
the values of *from_addr* and *to_addrs*, ``send_message`` deletes any Bcc *to_adresses* combines the values (if any) of the :mailheader:`To`,
field from *msg*. It then serializes *msg* using :mailheader:`Cc`, and :mailheader:`Bcc` fields from *msg*. If exactly one
set of :mailheader:`Resent-*` headers appear in the message, the regular
headers are ignored and the :mailheader:`Resent-*` headers are used instead.
If the message contains more than one set of :mailheader:`Resent-*` headers,
a :exc:`ValueError` is raised, since there is no way to unambiguously detect
the most recent set of :mailheader:`Resent-` headers.
``send_message`` serializes *msg* using
:class:`~email.generator.BytesGenerator` with ``\r\n`` as the *linesep*, and :class:`~email.generator.BytesGenerator` with ``\r\n`` as the *linesep*, and
calls :meth:`sendmail` to transmit the resulting message. calls :meth:`sendmail` to transmit the resulting message. Regardless of the
values of *from_addr* and *to_addrs*, ``send_message`` does not transmit any
:mailheader:`Bcc` or :mailheader:`Resent-Bcc` headers that may appear
in *msg*.
.. versionadded:: 3.2 .. versionadded:: 3.2

View File

@ -615,43 +615,43 @@ Row Objects
Let's assume we initialize a table as in the example given above:: Let's assume we initialize a table as in the example given above::
conn = sqlite3.connect(":memory:") conn = sqlite3.connect(":memory:")
c = conn.cursor() c = conn.cursor()
c.execute('''create table stocks c.execute('''create table stocks
(date text, trans text, symbol text, (date text, trans text, symbol text,
qty real, price real)''') qty real, price real)''')
c.execute("""insert into stocks c.execute("""insert into stocks
values ('2006-01-05','BUY','RHAT',100,35.14)""") values ('2006-01-05','BUY','RHAT',100,35.14)""")
conn.commit() conn.commit()
c.close() c.close()
Now we plug :class:`Row` in:: Now we plug :class:`Row` in::
>>> conn.row_factory = sqlite3.Row >>> conn.row_factory = sqlite3.Row
>>> c = conn.cursor() >>> c = conn.cursor()
>>> c.execute('select * from stocks') >>> c.execute('select * from stocks')
<sqlite3.Cursor object at 0x7f4e7dd8fa80> <sqlite3.Cursor object at 0x7f4e7dd8fa80>
>>> r = c.fetchone() >>> r = c.fetchone()
>>> type(r) >>> type(r)
<class 'sqlite3.Row'> <class 'sqlite3.Row'>
>>> tuple(r) >>> tuple(r)
('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14) ('2006-01-05', 'BUY', 'RHAT', 100.0, 35.14)
>>> len(r) >>> len(r)
5 5
>>> r[2] >>> r[2]
'RHAT' 'RHAT'
>>> r.keys() >>> r.keys()
['date', 'trans', 'symbol', 'qty', 'price'] ['date', 'trans', 'symbol', 'qty', 'price']
>>> r['qty'] >>> r['qty']
100.0 100.0
>>> for member in r: >>> for member in r:
... print(member) ... print(member)
... ...
2006-01-05 2006-01-05
BUY BUY
RHAT RHAT
100.0 100.0
35.14 35.14
.. _sqlite3-types: .. _sqlite3-types:
@ -902,6 +902,7 @@ only makes sense to call from a different thread.
.. rubric:: Footnotes .. rubric:: Footnotes
.. [#f1] The sqlite3 module is not built with loadable extension support by .. [#f1] The sqlite3 module is not built with loadable extension support by
default, because some platforms (notably Mac OS X) have SQLite libraries which default, because some platforms (notably Mac OS X) have SQLite
are compiled without this feature. To get loadable extension support, you must libraries which are compiled without this feature. To get loadable
pass --enable-loadable-sqlite-extensions to configure. extension support, you must pass --enable-loadable-sqlite-extensions to
configure.

View File

@ -206,7 +206,7 @@ handle NAT with non-secure FTP without opening fixed ports.
shutil shutil
------ ------
The :mod:`shutil` module has a new :func:`~shutil.disk_usage` providing total, The :mod:`shutil` module has a new :func:`~shutil.disk_usage` function providing total,
used and free disk space statistics. used and free disk space statistics.
(Contributed by Giampaolo Rodolà in :issue:`12442`) (Contributed by Giampaolo Rodolà in :issue:`12442`)

View File

@ -50,7 +50,7 @@ import os
from concurrent.futures import _base from concurrent.futures import _base
import queue import queue
import multiprocessing import multiprocessing
from multiprocessing.queues import SimpleQueue, SentinelReady from multiprocessing.queues import SimpleQueue, SentinelReady, Full
import threading import threading
import weakref import weakref
@ -195,15 +195,18 @@ def _queue_management_worker(executor_reference,
result_queue: A multiprocessing.Queue of _ResultItems generated by the result_queue: A multiprocessing.Queue of _ResultItems generated by the
process workers. process workers.
""" """
executor = None
def shutting_down():
return _shutdown or executor is None or executor._shutdown_thread
def shutdown_worker(): def shutdown_worker():
# This is an upper bound # This is an upper bound
nb_children_alive = sum(p.is_alive() for p in processes.values()) nb_children_alive = sum(p.is_alive() for p in processes.values())
for i in range(0, nb_children_alive): for i in range(0, nb_children_alive):
call_queue.put(None) call_queue.put_nowait(None)
# If .join() is not called on the created processes then # If .join() is not called on the created processes then
# some multiprocessing.Queue methods may deadlock on Mac OS # some multiprocessing.Queue methods may deadlock on Mac OS X.
# X.
for p in processes.values(): for p in processes.values():
p.join() p.join()
@ -222,7 +225,7 @@ def _queue_management_worker(executor_reference,
if executor is not None: if executor is not None:
executor._broken = True executor._broken = True
executor._shutdown_thread = True executor._shutdown_thread = True
del executor executor = None
# All futures in flight must be marked failed # All futures in flight must be marked failed
for work_id, work_item in pending_work_items.items(): for work_id, work_item in pending_work_items.items():
work_item.future.set_exception( work_item.future.set_exception(
@ -242,7 +245,11 @@ def _queue_management_worker(executor_reference,
if isinstance(result_item, int): if isinstance(result_item, int):
# Clean shutdown of a worker using its PID # Clean shutdown of a worker using its PID
# (avoids marking the executor broken) # (avoids marking the executor broken)
assert shutting_down()
del processes[result_item] del processes[result_item]
if not processes:
shutdown_worker()
return
elif result_item is not None: elif result_item is not None:
work_item = pending_work_items.pop(result_item.work_id, None) work_item = pending_work_items.pop(result_item.work_id, None)
# work_item can be None if another process terminated (see above) # work_item can be None if another process terminated (see above)
@ -257,16 +264,21 @@ def _queue_management_worker(executor_reference,
# - The interpreter is shutting down OR # - The interpreter is shutting down OR
# - The executor that owns this worker has been collected OR # - The executor that owns this worker has been collected OR
# - The executor that owns this worker has been shutdown. # - The executor that owns this worker has been shutdown.
if _shutdown or executor is None or executor._shutdown_thread: if shutting_down():
# Since no new work items can be added, it is safe to shutdown try:
# this thread if there are no pending work items. # Since no new work items can be added, it is safe to shutdown
if not pending_work_items: # this thread if there are no pending work items.
shutdown_worker() if not pending_work_items:
return shutdown_worker()
else: return
# Start shutting down by telling a process it can exit. else:
call_queue.put(None) # Start shutting down by telling a process it can exit.
del executor call_queue.put_nowait(None)
except Full:
# This is not a problem: we will eventually be woken up (in
# result_queue.get()) and be able to send a sentinel again.
pass
executor = None
_system_limits_checked = False _system_limits_checked = False
_system_limited = None _system_limited = None

View File

@ -214,7 +214,7 @@ class BadBytecodeTest(unittest.TestCase):
lambda bc: bc[:8] + b'<test>', lambda bc: bc[:8] + b'<test>',
del_source=del_source) del_source=del_source)
file_path = mapping['_temp'] if not del_source else bytecode_path file_path = mapping['_temp'] if not del_source else bytecode_path
with self.assertRaises(ValueError): with self.assertRaises(EOFError):
self.import_(file_path, '_temp') self.import_(file_path, '_temp')
def _test_bad_magic(self, test, *, del_source=False): def _test_bad_magic(self, test, *, del_source=False):

52
Lib/smtplib.py Executable file → Normal file
View File

@ -49,6 +49,7 @@ import email.message
import email.generator import email.generator
import base64 import base64
import hmac import hmac
import copy
from email.base64mime import body_encode as encode_base64 from email.base64mime import body_encode as encode_base64
from sys import stderr from sys import stderr
@ -676,7 +677,7 @@ class SMTP:
msg may be a string containing characters in the ASCII range, or a byte 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 string. A string is encoded to bytes using the ascii codec, and lone
\r and \n characters are converted to \r\n characters. \\r and \\n characters are converted to \\r\\n characters.
If there has been no previous EHLO or HELO command this session, this 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 method tries ESMTP EHLO first. If the server does ESMTP, message size
@ -759,24 +760,49 @@ class SMTP:
"""Converts message to a bytestring and passes it to sendmail. """Converts message to a bytestring and passes it to sendmail.
The arguments are as for sendmail, except that msg is an The arguments are as for sendmail, except that msg is an
email.message.Message object. If from_addr is None, the from_addr is email.message.Message object. If from_addr is None or to_addrs is
taken from the 'From' header of the Message. If to_addrs is None, its None, these arguments are taken from the headers of the Message as
value is composed from the addresses listed in the 'To', 'CC', and described in RFC 2822 (a ValueError is raised if there is more than
'Bcc' fields. Regardless of the values of from_addr and to_addr, any one set of 'Resent-' headers). Regardless of the values of from_addr and
Bcc field in the Message object is deleted. The Message object is then to_addr, any Bcc field (or Resent-Bcc field, when the Message is a
serialized using email.generator.BytesGenerator and sendmail is called resent) of the Message object won't be transmitted. The Message
to transmit the message. object is then serialized using email.generator.BytesGenerator and
sendmail is called to transmit the message.
""" """
# 'Resent-Date' is a mandatory field if the Message is resent (RFC 2822
# Section 3.6.6). In such a case, we use the 'Resent-*' fields. However,
# if there is more than one 'Resent-' block there's no way to
# unambiguously determine which one is the most recent in all cases,
# so rather than guess we raise a ValueError in that case.
#
# TODO implement heuristics to guess the correct Resent-* block with an
# option allowing the user to enable the heuristics. (It should be
# possible to guess correctly almost all of the time.)
resent =msg.get_all('Resent-Date')
if resent is None:
header_prefix = ''
elif len(resent) == 1:
header_prefix = 'Resent-'
else:
raise ValueError("message has more than one 'Resent-' header block")
if from_addr is None: if from_addr is None:
from_addr = msg['From'] # Prefer the sender field per RFC 2822:3.6.2.
from_addr = (msg[header_prefix+'Sender']
if (header_prefix+'Sender') in msg
else msg[header_prefix+'From'])
if to_addrs is None: if to_addrs is None:
addr_fields = [f for f in (msg['To'], msg['Bcc'], msg['CC']) addr_fields = [f for f in (msg[header_prefix+'To'],
if f is not None] msg[header_prefix+'Bcc'],
msg[header_prefix+'Cc']) if f is not None]
to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)] to_addrs = [a[1] for a in email.utils.getaddresses(addr_fields)]
del msg['Bcc'] # Make a local copy so we can delete the bcc headers.
msg_copy = copy.copy(msg)
del msg_copy['Bcc']
del msg_copy['Resent-Bcc']
with io.BytesIO() as bytesmsg: with io.BytesIO() as bytesmsg:
g = email.generator.BytesGenerator(bytesmsg) g = email.generator.BytesGenerator(bytesmsg)
g.flatten(msg, linesep='\r\n') g.flatten(msg_copy, linesep='\r\n')
flatmsg = bytesmsg.getvalue() flatmsg = bytesmsg.getvalue()
return self.sendmail(from_addr, to_addrs, flatmsg, mail_options, return self.sendmail(from_addr, to_addrs, flatmsg, mail_options,
rcpt_options) rcpt_options)

View File

@ -367,6 +367,13 @@ class ExecutorTest(unittest.TestCase):
self.assertEqual([None, None], results) self.assertEqual([None, None], results)
def test_shutdown_race_issue12456(self):
# Issue #12456: race condition at shutdown where trying to post a
# sentinel in the call queue blocks (the queue is full while processes
# have exited).
self.executor.map(str, [2] * (self.worker_count + 1))
self.executor.shutdown()
class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest): class ThreadPoolExecutorTest(ThreadPoolMixin, ExecutorTest):
def test_map_submits_without_iteration(self): def test_map_submits_without_iteration(self):

View File

@ -459,20 +459,20 @@ class TestDialectExcel(TestCsvBase):
'5', '6']]) '5', '6']])
def test_quoted_quote(self): def test_quoted_quote(self):
self.readerAssertEqual('1,2,3,"""I see,"" said the blind man","as he picked up his hammer and saw"', self.readerAssertEqual('1,2,3,"""I see,"" said the happy man","as he picked up his hammer and saw"',
[['1', '2', '3', [['1', '2', '3',
'"I see," said the blind man', '"I see," said the happy man',
'as he picked up his hammer and saw']]) 'as he picked up his hammer and saw']])
def test_quoted_nl(self): def test_quoted_nl(self):
input = '''\ input = '''\
1,2,3,"""I see,"" 1,2,3,"""I see,""
said the blind man","as he picked up his said the happy man","as he picked up his
hammer and saw" hammer and saw"
9,8,7,6''' 9,8,7,6'''
self.readerAssertEqual(input, self.readerAssertEqual(input,
[['1', '2', '3', [['1', '2', '3',
'"I see,"\nsaid the blind man', '"I see,"\nsaid the happy man',
'as he picked up his\nhammer and saw'], 'as he picked up his\nhammer and saw'],
['9','8','7','6']]) ['9','8','7','6']])

View File

@ -228,6 +228,30 @@ class BugsTestCase(unittest.TestCase):
invalid_string = b'l\x02\x00\x00\x00\x00\x00\x00\x00' invalid_string = b'l\x02\x00\x00\x00\x00\x00\x00\x00'
self.assertRaises(ValueError, marshal.loads, invalid_string) self.assertRaises(ValueError, marshal.loads, invalid_string)
def test_multiple_dumps_and_loads(self):
# Issue 12291: marshal.load() should be callable multiple times
# with interleaved data written by non-marshal code
# Adapted from a patch by Engelbert Gruber.
data = (1, 'abc', b'def', 1.0, (2, 'a', ['b', b'c']))
for interleaved in (b'', b'0123'):
ilen = len(interleaved)
positions = []
try:
with open(support.TESTFN, 'wb') as f:
for d in data:
marshal.dump(d, f)
if ilen:
f.write(interleaved)
positions.append(f.tell())
with open(support.TESTFN, 'rb') as f:
for i, d in enumerate(data):
self.assertEqual(d, marshal.load(f))
if ilen:
f.read(ilen)
self.assertEqual(positions[i], f.tell())
finally:
support.unlink(support.TESTFN)
def test_main(): def test_main():
support.run_unittest(IntTestCase, support.run_unittest(IntTestCase,

View File

@ -732,11 +732,11 @@ class TestShutil(unittest.TestCase):
"disk_usage not available on this platform") "disk_usage not available on this platform")
def test_disk_usage(self): def test_disk_usage(self):
usage = shutil.disk_usage(os.getcwd()) usage = shutil.disk_usage(os.getcwd())
self.assertTrue(usage.total > 0) self.assertGreater(usage.total, 0)
self.assertTrue(usage.used > 0) self.assertGreater(usage.used, 0)
self.assertTrue(usage.free >= 0) self.assertGreaterEqual(usage.free, 0)
self.assertTrue(usage.total >= usage.used) self.assertGreaterEqual(usage.total, usage.used)
self.assertTrue(usage.total > usage.free) self.assertGreater(usage.total, usage.free)
class TestMove(unittest.TestCase): class TestMove(unittest.TestCase):

View File

@ -320,13 +320,16 @@ class DebuggingServerTests(unittest.TestCase):
# XXX (see comment in testSend) # XXX (see comment in testSend)
time.sleep(0.01) time.sleep(0.01)
smtp.quit() smtp.quit()
# make sure the Bcc header is still in the message.
self.assertEqual(m['Bcc'], 'John Root <root@localhost>, "Dinsdale" '
'<warped@silly.walks.com>')
self.client_evt.set() self.client_evt.set()
self.serv_evt.wait() self.serv_evt.wait()
self.output.flush() self.output.flush()
# Add the X-Peer header that DebuggingServer adds # Add the X-Peer header that DebuggingServer adds
m['X-Peer'] = socket.gethostbyname('localhost') m['X-Peer'] = socket.gethostbyname('localhost')
# The Bcc header is deleted before serialization. # The Bcc header should not be transmitted.
del m['Bcc'] del m['Bcc']
mexpect = '%s%s\n%s' % (MSG_BEGIN, m.as_string(), MSG_END) mexpect = '%s%s\n%s' % (MSG_BEGIN, m.as_string(), MSG_END)
self.assertEqual(self.output.getvalue(), mexpect) self.assertEqual(self.output.getvalue(), mexpect)
@ -365,6 +368,112 @@ class DebuggingServerTests(unittest.TestCase):
re.MULTILINE) re.MULTILINE)
self.assertRegex(debugout, to_addr) self.assertRegex(debugout, to_addr)
def testSendMessageWithSpecifiedAddresses(self):
# Make sure addresses specified in call override those in message.
m = email.mime.text.MIMEText('A test message')
m['From'] = 'foo@bar.com'
m['To'] = 'John, Dinsdale'
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
smtp.send_message(m, from_addr='joe@example.com', to_addrs='foo@example.net')
# 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
m['X-Peer'] = socket.gethostbyname('localhost')
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: joe@example.com$", re.MULTILINE)
self.assertRegex(debugout, sender)
for addr in ('John', 'Dinsdale'):
to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
re.MULTILINE)
self.assertNotRegex(debugout, to_addr)
recip = re.compile(r"^recips: .*'foo@example.net'.*$", re.MULTILINE)
self.assertRegex(debugout, recip)
def testSendMessageWithMultipleFrom(self):
# Sender overrides To
m = email.mime.text.MIMEText('A test message')
m['From'] = 'Bernard, Bianca'
m['Sender'] = 'the_rescuers@Rescue-Aid-Society.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
m['X-Peer'] = socket.gethostbyname('localhost')
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: the_rescuers@Rescue-Aid-Society.com$", re.MULTILINE)
self.assertRegex(debugout, sender)
for addr in ('John', 'Dinsdale'):
to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
re.MULTILINE)
self.assertRegex(debugout, to_addr)
def testSendMessageResent(self):
m = email.mime.text.MIMEText('A test message')
m['From'] = 'foo@bar.com'
m['To'] = 'John'
m['CC'] = 'Sally, Fred'
m['Bcc'] = 'John Root <root@localhost>, "Dinsdale" <warped@silly.walks.com>'
m['Resent-Date'] = 'Thu, 1 Jan 1970 17:42:00 +0000'
m['Resent-From'] = 'holy@grail.net'
m['Resent-To'] = 'Martha <my_mom@great.cooker.com>, Jeff'
m['Resent-Bcc'] = 'doe@losthope.net'
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()
# The Resent-Bcc headers are deleted before serialization.
del m['Bcc']
del m['Resent-Bcc']
# Add the X-Peer header that DebuggingServer adds
m['X-Peer'] = socket.gethostbyname('localhost')
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: holy@grail.net$", re.MULTILINE)
self.assertRegex(debugout, sender)
for addr in ('my_mom@great.cooker.com', 'Jeff', 'doe@losthope.net'):
to_addr = re.compile(r"^recips: .*'{}'.*$".format(addr),
re.MULTILINE)
self.assertRegex(debugout, to_addr)
def testSendMessageMultipleResentRaises(self):
m = email.mime.text.MIMEText('A test message')
m['From'] = 'foo@bar.com'
m['To'] = 'John'
m['CC'] = 'Sally, Fred'
m['Bcc'] = 'John Root <root@localhost>, "Dinsdale" <warped@silly.walks.com>'
m['Resent-Date'] = 'Thu, 1 Jan 1970 17:42:00 +0000'
m['Resent-From'] = 'holy@grail.net'
m['Resent-To'] = 'Martha <my_mom@great.cooker.com>, Jeff'
m['Resent-Bcc'] = 'doe@losthope.net'
m['Resent-Date'] = 'Thu, 2 Jan 1970 17:42:00 +0000'
m['Resent-To'] = 'holy@grail.net'
m['Resent-From'] = 'Martha <my_mom@great.cooker.com>, Jeff'
smtp = smtplib.SMTP(HOST, self.port, local_hostname='localhost', timeout=3)
with self.assertRaises(ValueError):
smtp.send_message(m)
smtp.close()
class NonConnectingTests(unittest.TestCase): class NonConnectingTests(unittest.TestCase):

View File

@ -278,6 +278,7 @@ Ben Escoto
Andy Eskilsson Andy Eskilsson
André Espaze André Espaze
Stefan Esser Stefan Esser
Nicolas Estibals
Stephen D Evans Stephen D Evans
Carey Evans Carey Evans
Tim Everett Tim Everett

View File

@ -10,6 +10,9 @@ What's New in Python 3.3 Alpha 1?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #12291: You can now load multiple marshalled objects from a stream,
with other data interleaved between marshalled objects.
- Issue #12356: When required positional or keyword-only arguments are not - Issue #12356: When required positional or keyword-only arguments are not
given, produce a informative error message which includes the name(s) of the given, produce a informative error message which includes the name(s) of the
missing arguments. missing arguments.
@ -200,6 +203,9 @@ Core and Builtins
Library Library
------- -------
- Issue #12147: Adjust the new-in-3.2 smtplib.send_message method for better
conformance to the RFCs: correctly handle Sender and Resent- headers.
- Issue #12352: Fix a deadlock in multiprocessing.Heap when a block is freed by - Issue #12352: Fix a deadlock in multiprocessing.Heap when a block is freed by
the garbage collector while the Heap lock is held. the garbage collector while the Heap lock is held.

View File

@ -57,6 +57,7 @@ typedef struct {
int error; /* see WFERR_* values */ int error; /* see WFERR_* values */
int depth; int depth;
/* If fp == NULL, the following are valid: */ /* If fp == NULL, the following are valid: */
PyObject * readable; /* Stream-like object being read from */
PyObject *str; PyObject *str;
PyObject *current_filename; PyObject *current_filename;
char *ptr; char *ptr;
@ -463,27 +464,75 @@ typedef WFILE RFILE; /* Same struct with different invariants */
#define rs_byte(p) (((p)->ptr < (p)->end) ? (unsigned char)*(p)->ptr++ : EOF) #define rs_byte(p) (((p)->ptr < (p)->end) ? (unsigned char)*(p)->ptr++ : EOF)
#define r_byte(p) ((p)->fp ? getc((p)->fp) : rs_byte(p))
static int static int
r_string(char *s, int n, RFILE *p) r_string(char *s, int n, RFILE *p)
{ {
if (p->fp != NULL) char * ptr;
/* The result fits into int because it must be <=n. */ int read, left;
return (int)fread(s, 1, n, p->fp);
if (p->end - p->ptr < n) if (!p->readable) {
n = (int)(p->end - p->ptr); if (p->fp != NULL)
memcpy(s, p->ptr, n); /* The result fits into int because it must be <=n. */
p->ptr += n; read = (int) fread(s, 1, n, p->fp);
return n; else {
left = (int)(p->end - p->ptr);
read = (left < n) ? left : n;
memcpy(s, p->ptr, read);
p->ptr += read;
}
}
else {
PyObject *data = PyObject_CallMethod(p->readable, "read", "i", n);
read = 0;
if (data != NULL) {
if (!PyBytes_Check(data)) {
PyErr_Format(PyExc_TypeError,
"f.read() returned not bytes but %.100s",
data->ob_type->tp_name);
}
else {
read = PyBytes_GET_SIZE(data);
if (read > 0) {
ptr = PyBytes_AS_STRING(data);
memcpy(s, ptr, read);
}
}
Py_DECREF(data);
}
}
if (!PyErr_Occurred() && (read < n)) {
PyErr_SetString(PyExc_EOFError, "EOF read where not expected");
}
return read;
}
static int
r_byte(RFILE *p)
{
int c = EOF;
unsigned char ch;
int n;
if (!p->readable)
c = p->fp ? getc(p->fp) : rs_byte(p);
else {
n = r_string((char *) &ch, 1, p);
if (n > 0)
c = ch;
}
return c;
} }
static int static int
r_short(RFILE *p) r_short(RFILE *p)
{ {
register short x; register short x;
x = r_byte(p); unsigned char buffer[2];
x |= r_byte(p) << 8;
r_string((char *) buffer, 2, p);
x = buffer[0];
x |= buffer[1] << 8;
/* Sign-extension, in case short greater than 16 bits */ /* Sign-extension, in case short greater than 16 bits */
x |= -(x & 0x8000); x |= -(x & 0x8000);
return x; return x;
@ -493,19 +542,13 @@ static long
r_long(RFILE *p) r_long(RFILE *p)
{ {
register long x; register long x;
register FILE *fp = p->fp; unsigned char buffer[4];
if (fp) {
x = getc(fp); r_string((char *) buffer, 4, p);
x |= (long)getc(fp) << 8; x = buffer[0];
x |= (long)getc(fp) << 16; x |= (long)buffer[1] << 8;
x |= (long)getc(fp) << 24; x |= (long)buffer[2] << 16;
} x |= (long)buffer[3] << 24;
else {
x = rs_byte(p);
x |= (long)rs_byte(p) << 8;
x |= (long)rs_byte(p) << 16;
x |= (long)rs_byte(p) << 24;
}
#if SIZEOF_LONG > 4 #if SIZEOF_LONG > 4
/* Sign extension for 64-bit machines */ /* Sign extension for 64-bit machines */
x |= -(x & 0x80000000L); x |= -(x & 0x80000000L);
@ -523,25 +566,30 @@ r_long(RFILE *p)
static PyObject * static PyObject *
r_long64(RFILE *p) r_long64(RFILE *p)
{ {
PyObject * result = NULL;
long lo4 = r_long(p); long lo4 = r_long(p);
long hi4 = r_long(p); long hi4 = r_long(p);
if (!PyErr_Occurred()) {
#if SIZEOF_LONG > 4 #if SIZEOF_LONG > 4
long x = (hi4 << 32) | (lo4 & 0xFFFFFFFFL); long x = (hi4 << 32) | (lo4 & 0xFFFFFFFFL);
return PyLong_FromLong(x); result = PyLong_FromLong(x);
#else #else
unsigned char buf[8]; unsigned char buf[8];
int one = 1; int one = 1;
int is_little_endian = (int)*(char*)&one; int is_little_endian = (int)*(char*)&one;
if (is_little_endian) { if (is_little_endian) {
memcpy(buf, &lo4, 4); memcpy(buf, &lo4, 4);
memcpy(buf+4, &hi4, 4); memcpy(buf+4, &hi4, 4);
} }
else { else {
memcpy(buf, &hi4, 4); memcpy(buf, &hi4, 4);
memcpy(buf+4, &lo4, 4); memcpy(buf+4, &lo4, 4);
} }
return _PyLong_FromByteArray(buf, 8, is_little_endian, 1); result = _PyLong_FromByteArray(buf, 8, is_little_endian, 1);
#endif #endif
}
return result;
} }
static PyObject * static PyObject *
@ -553,6 +601,8 @@ r_PyLong(RFILE *p)
digit d; digit d;
n = r_long(p); n = r_long(p);
if (PyErr_Occurred())
return NULL;
if (n == 0) if (n == 0)
return (PyObject *)_PyLong_New(0); return (PyObject *)_PyLong_New(0);
if (n < -INT_MAX || n > INT_MAX) { if (n < -INT_MAX || n > INT_MAX) {
@ -572,6 +622,8 @@ r_PyLong(RFILE *p)
d = 0; d = 0;
for (j=0; j < PyLong_MARSHAL_RATIO; j++) { for (j=0; j < PyLong_MARSHAL_RATIO; j++) {
md = r_short(p); md = r_short(p);
if (PyErr_Occurred())
break;
if (md < 0 || md > PyLong_MARSHAL_BASE) if (md < 0 || md > PyLong_MARSHAL_BASE)
goto bad_digit; goto bad_digit;
d += (digit)md << j*PyLong_MARSHAL_SHIFT; d += (digit)md << j*PyLong_MARSHAL_SHIFT;
@ -581,6 +633,8 @@ r_PyLong(RFILE *p)
d = 0; d = 0;
for (j=0; j < shorts_in_top_digit; j++) { for (j=0; j < shorts_in_top_digit; j++) {
md = r_short(p); md = r_short(p);
if (PyErr_Occurred())
break;
if (md < 0 || md > PyLong_MARSHAL_BASE) if (md < 0 || md > PyLong_MARSHAL_BASE)
goto bad_digit; goto bad_digit;
/* topmost marshal digit should be nonzero */ /* topmost marshal digit should be nonzero */
@ -592,6 +646,10 @@ r_PyLong(RFILE *p)
} }
d += (digit)md << j*PyLong_MARSHAL_SHIFT; d += (digit)md << j*PyLong_MARSHAL_SHIFT;
} }
if (PyErr_Occurred()) {
Py_DECREF(ob);
return NULL;
}
/* top digit should be nonzero, else the resulting PyLong won't be /* top digit should be nonzero, else the resulting PyLong won't be
normalized */ normalized */
ob->ob_digit[size-1] = d; ob->ob_digit[size-1] = d;
@ -660,7 +718,8 @@ r_object(RFILE *p)
break; break;
case TYPE_INT: case TYPE_INT:
retval = PyLong_FromLong(r_long(p)); n = r_long(p);
retval = PyErr_Occurred() ? NULL : PyLong_FromLong(n);
break; break;
case TYPE_INT64: case TYPE_INT64:
@ -770,6 +829,10 @@ r_object(RFILE *p)
case TYPE_STRING: case TYPE_STRING:
n = r_long(p); n = r_long(p);
if (PyErr_Occurred()) {
retval = NULL;
break;
}
if (n < 0 || n > INT_MAX) { if (n < 0 || n > INT_MAX) {
PyErr_SetString(PyExc_ValueError, "bad marshal data (string size out of range)"); PyErr_SetString(PyExc_ValueError, "bad marshal data (string size out of range)");
retval = NULL; retval = NULL;
@ -795,6 +858,10 @@ r_object(RFILE *p)
char *buffer; char *buffer;
n = r_long(p); n = r_long(p);
if (PyErr_Occurred()) {
retval = NULL;
break;
}
if (n < 0 || n > INT_MAX) { if (n < 0 || n > INT_MAX) {
PyErr_SetString(PyExc_ValueError, "bad marshal data (unicode size out of range)"); PyErr_SetString(PyExc_ValueError, "bad marshal data (unicode size out of range)");
retval = NULL; retval = NULL;
@ -820,6 +887,10 @@ r_object(RFILE *p)
case TYPE_TUPLE: case TYPE_TUPLE:
n = r_long(p); n = r_long(p);
if (PyErr_Occurred()) {
retval = NULL;
break;
}
if (n < 0 || n > INT_MAX) { if (n < 0 || n > INT_MAX) {
PyErr_SetString(PyExc_ValueError, "bad marshal data (tuple size out of range)"); PyErr_SetString(PyExc_ValueError, "bad marshal data (tuple size out of range)");
retval = NULL; retval = NULL;
@ -847,6 +918,10 @@ r_object(RFILE *p)
case TYPE_LIST: case TYPE_LIST:
n = r_long(p); n = r_long(p);
if (PyErr_Occurred()) {
retval = NULL;
break;
}
if (n < 0 || n > INT_MAX) { if (n < 0 || n > INT_MAX) {
PyErr_SetString(PyExc_ValueError, "bad marshal data (list size out of range)"); PyErr_SetString(PyExc_ValueError, "bad marshal data (list size out of range)");
retval = NULL; retval = NULL;
@ -899,6 +974,10 @@ r_object(RFILE *p)
case TYPE_SET: case TYPE_SET:
case TYPE_FROZENSET: case TYPE_FROZENSET:
n = r_long(p); n = r_long(p);
if (PyErr_Occurred()) {
retval = NULL;
break;
}
if (n < 0 || n > INT_MAX) { if (n < 0 || n > INT_MAX) {
PyErr_SetString(PyExc_ValueError, "bad marshal data (set size out of range)"); PyErr_SetString(PyExc_ValueError, "bad marshal data (set size out of range)");
retval = NULL; retval = NULL;
@ -952,10 +1031,20 @@ r_object(RFILE *p)
/* XXX ignore long->int overflows for now */ /* XXX ignore long->int overflows for now */
argcount = (int)r_long(p); argcount = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
kwonlyargcount = (int)r_long(p); kwonlyargcount = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
nlocals = (int)r_long(p); nlocals = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
stacksize = (int)r_long(p); stacksize = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
flags = (int)r_long(p); flags = (int)r_long(p);
if (PyErr_Occurred())
goto code_error;
code = r_object(p); code = r_object(p);
if (code == NULL) if (code == NULL)
goto code_error; goto code_error;
@ -1049,6 +1138,7 @@ PyMarshal_ReadShortFromFile(FILE *fp)
{ {
RFILE rf; RFILE rf;
assert(fp); assert(fp);
rf.readable = NULL;
rf.fp = fp; rf.fp = fp;
rf.current_filename = NULL; rf.current_filename = NULL;
rf.end = rf.ptr = NULL; rf.end = rf.ptr = NULL;
@ -1060,6 +1150,7 @@ PyMarshal_ReadLongFromFile(FILE *fp)
{ {
RFILE rf; RFILE rf;
rf.fp = fp; rf.fp = fp;
rf.readable = NULL;
rf.current_filename = NULL; rf.current_filename = NULL;
rf.ptr = rf.end = NULL; rf.ptr = rf.end = NULL;
return r_long(&rf); return r_long(&rf);
@ -1121,6 +1212,7 @@ PyMarshal_ReadObjectFromFile(FILE *fp)
RFILE rf; RFILE rf;
PyObject *result; PyObject *result;
rf.fp = fp; rf.fp = fp;
rf.readable = NULL;
rf.current_filename = NULL; rf.current_filename = NULL;
rf.depth = 0; rf.depth = 0;
rf.ptr = rf.end = NULL; rf.ptr = rf.end = NULL;
@ -1134,6 +1226,7 @@ PyMarshal_ReadObjectFromString(char *str, Py_ssize_t len)
RFILE rf; RFILE rf;
PyObject *result; PyObject *result;
rf.fp = NULL; rf.fp = NULL;
rf.readable = NULL;
rf.current_filename = NULL; rf.current_filename = NULL;
rf.ptr = str; rf.ptr = str;
rf.end = str + len; rf.end = str + len;
@ -1149,6 +1242,7 @@ PyMarshal_WriteObjectToString(PyObject *x, int version)
PyObject *res = NULL; PyObject *res = NULL;
wf.fp = NULL; wf.fp = NULL;
wf.readable = NULL;
wf.str = PyBytes_FromStringAndSize((char *)NULL, 50); wf.str = PyBytes_FromStringAndSize((char *)NULL, 50);
if (wf.str == NULL) if (wf.str == NULL)
return NULL; return NULL;
@ -1224,32 +1318,30 @@ The version argument indicates the data format that dump should use.");
static PyObject * static PyObject *
marshal_load(PyObject *self, PyObject *f) marshal_load(PyObject *self, PyObject *f)
{ {
/* XXX Quick hack -- need to do this differently */
PyObject *data, *result; PyObject *data, *result;
RFILE rf; RFILE rf;
data = PyObject_CallMethod(f, "read", "");
/*
* Make a call to the read method, but read zero bytes.
* This is to ensure that the object passed in at least
* has a read method which returns bytes.
*/
data = PyObject_CallMethod(f, "read", "i", 0);
if (data == NULL) if (data == NULL)
return NULL; return NULL;
rf.fp = NULL; if (!PyBytes_Check(data)) {
rf.current_filename = NULL; PyErr_Format(PyExc_TypeError,
if (PyBytes_Check(data)) { "f.read() returned not bytes but %.100s",
rf.ptr = PyBytes_AS_STRING(data); data->ob_type->tp_name);
rf.end = rf.ptr + PyBytes_GET_SIZE(data); result = NULL;
}
else if (PyBytes_Check(data)) {
rf.ptr = PyBytes_AS_STRING(data);
rf.end = rf.ptr + PyBytes_GET_SIZE(data);
} }
else { else {
PyErr_Format(PyExc_TypeError, rf.depth = 0;
"f.read() returned neither string " rf.fp = NULL;
"nor bytes but %.100s", rf.readable = f;
data->ob_type->tp_name); rf.current_filename = NULL;
Py_DECREF(data); result = read_object(&rf);
return NULL;
} }
rf.depth = 0;
result = read_object(&rf);
Py_DECREF(data); Py_DECREF(data);
return result; return result;
} }
@ -1300,6 +1392,7 @@ marshal_loads(PyObject *self, PyObject *args)
s = p.buf; s = p.buf;
n = p.len; n = p.len;
rf.fp = NULL; rf.fp = NULL;
rf.readable = NULL;
rf.current_filename = NULL; rf.current_filename = NULL;
rf.ptr = s; rf.ptr = s;
rf.end = s + n; rf.end = s + n;

View File

@ -119,6 +119,7 @@ pythondll_uuid = {
"30":"{6953bc3b-6768-4291-8410-7914ce6e2ca8}", "30":"{6953bc3b-6768-4291-8410-7914ce6e2ca8}",
"31":"{4afcba0b-13e4-47c3-bebe-477428b46913}", "31":"{4afcba0b-13e4-47c3-bebe-477428b46913}",
"32":"{3ff95315-1096-4d31-bd86-601d5438ad5e}", "32":"{3ff95315-1096-4d31-bd86-601d5438ad5e}",
"33":"{f7581ca4-d368-4eea-8f82-d48c64c4f047}",
} [major+minor] } [major+minor]
# Compute the name that Sphinx gives to the docfile # Compute the name that Sphinx gives to the docfile
@ -1010,6 +1011,8 @@ def add_files(db):
lib.remove_pyc() lib.remove_pyc()
# package READMEs if present # package READMEs if present
lib.glob("README") lib.glob("README")
if dir=='Lib':
lib.add_file("sysconfig.cfg")
if dir=='test' and parent.physical=='Lib': if dir=='test' and parent.physical=='Lib':
lib.add_file("185test.db") lib.add_file("185test.db")
lib.add_file("audiotest.au") lib.add_file("audiotest.au")
@ -1045,7 +1048,7 @@ def add_files(db):
if dir=="Icons": if dir=="Icons":
lib.glob("*.gif") lib.glob("*.gif")
lib.add_file("idle.icns") lib.add_file("idle.icns")
if dir=="command" and parent.physical=="distutils": if dir=="command" and parent.physical in ("distutils", "packaging"):
lib.glob("wininst*.exe") lib.glob("wininst*.exe")
lib.add_file("command_template") lib.add_file("command_template")
if dir=="lib2to3": if dir=="lib2to3":
@ -1156,6 +1159,8 @@ def add_files(db):
lib.add_file("README.txt", src="README") lib.add_file("README.txt", src="README")
if f == 'Scripts': if f == 'Scripts':
lib.add_file("2to3.py", src="2to3") lib.add_file("2to3.py", src="2to3")
lib.add_file("pydoc3.py", src="pydoc3")
lib.add_file("pysetup3.py", src="pysetup3")
if have_tcl: if have_tcl:
lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw") lib.start_component("pydocgui.pyw", tcltk, keyfile="pydocgui.pyw")
lib.add_file("pydocgui.pyw") lib.add_file("pydocgui.pyw")