summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/stdtypes.rst86
-rw-r--r--Doc/reference/datamodel.rst11
-rw-r--r--Lib/email/quoprimime.py6
-rw-r--r--Lib/email/test/test_email.py207
-rw-r--r--Lib/random.py6
-rw-r--r--Lib/test/test_multiprocessing.py2
-rw-r--r--Lib/test/test_set.py34
-rw-r--r--Objects/setobject.c33
8 files changed, 312 insertions, 73 deletions
diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst
index 1b7d5ec..a1a3879 100644
--- a/Doc/library/stdtypes.rst
+++ b/Doc/library/stdtypes.rst
@@ -267,46 +267,46 @@ All numeric types (except complex) support the following operations, sorted by
ascending priority (operations in the same box have the same priority; all
numeric operations have a higher priority than comparison operations):
-+---------------------+---------------------------------+-------+--------------------+
-| Operation | Result | Notes | Full documentation |
-+=====================+=================================+=======+====================+
-| ``x + y`` | sum of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x - y`` | difference of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x * y`` | product of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x / y`` | quotient of *x* and *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x // y`` | floored quotient of *x* and | \(1) | |
-| | *y* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x % y`` | remainder of ``x / y`` | \(2) | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``-x`` | *x* negated | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``+x`` | *x* unchanged | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``abs(x)`` | absolute value or magnitude of | | :func:`abs` |
-| | *x* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``int(x)`` | *x* converted to integer | \(3) | :func:`int` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``float(x)`` | *x* converted to floating point | \(4) | :func:`float` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``complex(re, im)`` | a complex number with real part | | :func:`complex` |
-| | *re*, imaginary part *im*. | | |
-| | *im* defaults to zero. | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``c.conjugate()`` | conjugate of the complex number | | |
-| | *c* | | |
-+---------------------+---------------------------------+-------+--------------------+
-| ``divmod(x, y)`` | the pair ``(x // y, x % y)`` | \(2) | :func:`divmod` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``pow(x, y)`` | *x* to the power *y* | \(5) | :func:`pow` |
-+---------------------+---------------------------------+-------+--------------------+
-| ``x ** y`` | *x* to the power *y* | \(5) | |
-+---------------------+---------------------------------+-------+--------------------+
++---------------------+---------------------------------+---------+--------------------+
+| Operation | Result | Notes | Full documentation |
++=====================+=================================+=========+====================+
+| ``x + y`` | sum of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x - y`` | difference of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x * y`` | product of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x / y`` | quotient of *x* and *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x // y`` | floored quotient of *x* and | \(1) | |
+| | *y* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``x % y`` | remainder of ``x / y`` | \(2) | |
++---------------------+---------------------------------+---------+--------------------+
+| ``-x`` | *x* negated | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``+x`` | *x* unchanged | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``abs(x)`` | absolute value or magnitude of | | :func:`abs` |
+| | *x* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``int(x)`` | *x* converted to integer | \(3)\(6)| :func:`int` |
++---------------------+---------------------------------+---------+--------------------+
+| ``float(x)`` | *x* converted to floating point | \(4)\(6)| :func:`float` |
++---------------------+---------------------------------+---------+--------------------+
+| ``complex(re, im)`` | a complex number with real part | \(6) | :func:`complex` |
+| | *re*, imaginary part *im*. | | |
+| | *im* defaults to zero. | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``c.conjugate()`` | conjugate of the complex number | | |
+| | *c* | | |
++---------------------+---------------------------------+---------+--------------------+
+| ``divmod(x, y)`` | the pair ``(x // y, x % y)`` | \(2) | :func:`divmod` |
++---------------------+---------------------------------+---------+--------------------+
+| ``pow(x, y)`` | *x* to the power *y* | \(5) | :func:`pow` |
++---------------------+---------------------------------+---------+--------------------+
+| ``x ** y`` | *x* to the power *y* | \(5) | |
++---------------------+---------------------------------+---------+--------------------+
.. index::
triple: operations on; numeric; types
@@ -345,6 +345,12 @@ Notes:
Python defines ``pow(0, 0)`` and ``0 ** 0`` to be ``1``, as is common for
programming languages.
+(6)
+ The numeric literals accepted include the digits ``0`` to ``9`` or any
+ Unicode equivalent (code points with the ``Nd`` property).
+
+ See http://www.unicode.org/Public/6.0.0/ucd/extracted/DerivedNumericType.txt
+ for a complete list of code points with the ``Nd`` property.
All :class:`numbers.Real` types (:class:`int` and
diff --git a/Doc/reference/datamodel.rst b/Doc/reference/datamodel.rst
index dca01de..6f874b6 100644
--- a/Doc/reference/datamodel.rst
+++ b/Doc/reference/datamodel.rst
@@ -1353,10 +1353,11 @@ Implementing Descriptors
^^^^^^^^^^^^^^^^^^^^^^^^
The following methods only apply when an instance of the class containing the
-method (a so-called *descriptor* class) appears in the class dictionary of
-another class, known as the *owner* class. In the examples below, "the
-attribute" refers to the attribute whose name is the key of the property in the
-owner class' :attr:`__dict__`.
+method (a so-called *descriptor* class) appears in an *owner* class (the
+descriptor must be in either the owner's class dictionary or in the class
+dictionary for one of its parents). In the examples below, "the attribute"
+refers to the attribute whose name is the key of the property in the owner
+class' :attr:`__dict__`.
.. method:: object.__get__(self, instance, owner)
@@ -1419,7 +1420,7 @@ Super Binding
If ``a`` is an instance of :class:`super`, then the binding ``super(B,
obj).m()`` searches ``obj.__class__.__mro__`` for the base class ``A``
immediately preceding ``B`` and then invokes the descriptor with the call:
- ``A.__dict__['m'].__get__(obj, A)``.
+ ``A.__dict__['m'].__get__(obj, obj.__class__)``.
For instance bindings, the precedence of descriptor invocation depends on the
which descriptor methods are defined. A descriptor can define any combination
diff --git a/Lib/email/quoprimime.py b/Lib/email/quoprimime.py
index 85efc08..7c7711e 100644
--- a/Lib/email/quoprimime.py
+++ b/Lib/email/quoprimime.py
@@ -135,9 +135,9 @@ def header_encode(header_bytes, charset='iso-8859-1'):
charset names the character set to use in the RFC 2046 header. It
defaults to iso-8859-1.
"""
- # Return empty headers unchanged
+ # Return empty headers as an empty string.
if not header_bytes:
- return str(header_bytes)
+ return ''
# Iterate over every byte, encoding if necessary.
encoded = []
for octet in header_bytes:
@@ -268,7 +268,7 @@ def decode(encoded, eol=NL):
if i == n:
decoded += eol
# Special case if original string did not end with eol
- if not encoded.endswith(eol) and decoded.endswith(eol):
+ if encoded[-1] not in '\r\n' and decoded.endswith(eol):
decoded = decoded[:-1]
return decoded
diff --git a/Lib/email/test/test_email.py b/Lib/email/test/test_email.py
index 4268a25..cb3b0b3 100644
--- a/Lib/email/test/test_email.py
+++ b/Lib/email/test/test_email.py
@@ -2883,21 +2883,200 @@ class TestQuopri(unittest.TestCase):
c = chr(x)
self.assertEqual(quoprimime.unquote(quoprimime.quote(c)), c)
- def test_header_encode(self):
- eq = self.assertEqual
- he = quoprimime.header_encode
- eq(he(b'hello'), '=?iso-8859-1?q?hello?=')
- eq(he(b'hello', charset='iso-8859-2'), '=?iso-8859-2?q?hello?=')
- eq(he(b'hello\nworld'), '=?iso-8859-1?q?hello=0Aworld?=')
- # Test a non-ASCII character
- eq(he(b'hello\xc7there'), '=?iso-8859-1?q?hello=C7there?=')
+ def _test_header_encode(self, header, expected_encoded_header, charset=None):
+ if charset is None:
+ encoded_header = quoprimime.header_encode(header)
+ else:
+ encoded_header = quoprimime.header_encode(header, charset)
+ self.assertEqual(encoded_header, expected_encoded_header)
- def test_decode(self):
- eq = self.assertEqual
- eq(quoprimime.decode(''), '')
- eq(quoprimime.decode('hello'), 'hello')
- eq(quoprimime.decode('hello', 'X'), 'hello')
- eq(quoprimime.decode('hello\nworld', 'X'), 'helloXworld')
+ def test_header_encode_null(self):
+ self._test_header_encode(b'', '')
+
+ def test_header_encode_one_word(self):
+ self._test_header_encode(b'hello', '=?iso-8859-1?q?hello?=')
+
+ def test_header_encode_two_lines(self):
+ self._test_header_encode(b'hello\nworld',
+ '=?iso-8859-1?q?hello=0Aworld?=')
+
+ def test_header_encode_non_ascii(self):
+ self._test_header_encode(b'hello\xc7there',
+ '=?iso-8859-1?q?hello=C7there?=')
+
+ def test_header_encode_alt_charset(self):
+ self._test_header_encode(b'hello', '=?iso-8859-2?q?hello?=',
+ charset='iso-8859-2')
+
+ def _test_header_decode(self, encoded_header, expected_decoded_header):
+ decoded_header = quoprimime.header_decode(encoded_header)
+ self.assertEqual(decoded_header, expected_decoded_header)
+
+ def test_header_decode_null(self):
+ self._test_header_decode('', '')
+
+ def test_header_decode_one_word(self):
+ self._test_header_decode('hello', 'hello')
+
+ def test_header_decode_two_lines(self):
+ self._test_header_decode('hello=0Aworld', 'hello\nworld')
+
+ def test_header_decode_non_ascii(self):
+ self._test_header_decode('hello=C7there', 'hello\xc7there')
+
+ def _test_decode(self, encoded, expected_decoded, eol=None):
+ if eol is None:
+ decoded = quoprimime.decode(encoded)
+ else:
+ decoded = quoprimime.decode(encoded, eol=eol)
+ self.assertEqual(decoded, expected_decoded)
+
+ def test_decode_null_word(self):
+ self._test_decode('', '')
+
+ def test_decode_null_line_null_word(self):
+ self._test_decode('\r\n', '\n')
+
+ def test_decode_one_word(self):
+ self._test_decode('hello', 'hello')
+
+ def test_decode_one_word_eol(self):
+ self._test_decode('hello', 'hello', eol='X')
+
+ def test_decode_one_line(self):
+ self._test_decode('hello\r\n', 'hello\n')
+
+ def test_decode_one_line_lf(self):
+ self._test_decode('hello\n', 'hello\n')
+
+ def test_decode_one_line_cr(self):
+ self._test_decode('hello\r', 'hello\n')
+
+ def test_decode_one_line_nl(self):
+ self._test_decode('hello\n', 'helloX', eol='X')
+
+ def test_decode_one_line_crnl(self):
+ self._test_decode('hello\r\n', 'helloX', eol='X')
+
+ def test_decode_one_line_one_word(self):
+ self._test_decode('hello\r\nworld', 'hello\nworld')
+
+ def test_decode_one_line_one_word_eol(self):
+ self._test_decode('hello\r\nworld', 'helloXworld', eol='X')
+
+ def test_decode_two_lines(self):
+ self._test_decode('hello\r\nworld\r\n', 'hello\nworld\n')
+
+ def test_decode_two_lines_eol(self):
+ self._test_decode('hello\r\nworld\r\n', 'helloXworldX', eol='X')
+
+ def test_decode_one_long_line(self):
+ self._test_decode('Spam' * 250, 'Spam' * 250)
+
+ def test_decode_one_space(self):
+ self._test_decode(' ', '')
+
+ def test_decode_multiple_spaces(self):
+ self._test_decode(' ' * 5, '')
+
+ def test_decode_one_line_trailing_spaces(self):
+ self._test_decode('hello \r\n', 'hello\n')
+
+ def test_decode_two_lines_trailing_spaces(self):
+ self._test_decode('hello \r\nworld \r\n', 'hello\nworld\n')
+
+ def test_decode_quoted_word(self):
+ self._test_decode('=22quoted=20words=22', '"quoted words"')
+
+ def test_decode_uppercase_quoting(self):
+ self._test_decode('ab=CD=EF', 'ab\xcd\xef')
+
+ def test_decode_lowercase_quoting(self):
+ self._test_decode('ab=cd=ef', 'ab\xcd\xef')
+
+ def test_decode_soft_line_break(self):
+ self._test_decode('soft line=\r\nbreak', 'soft linebreak')
+
+ def test_decode_false_quoting(self):
+ self._test_decode('A=1,B=A ==> A+B==2', 'A=1,B=A ==> A+B==2')
+
+ def _test_encode(self, body, expected_encoded_body, maxlinelen=None, eol=None):
+ kwargs = {}
+ if maxlinelen is None:
+ # Use body_encode's default.
+ maxlinelen = 76
+ else:
+ kwargs['maxlinelen'] = maxlinelen
+ if eol is None:
+ # Use body_encode's default.
+ eol = '\n'
+ else:
+ kwargs['eol'] = eol
+ encoded_body = quoprimime.body_encode(body, **kwargs)
+ self.assertEqual(encoded_body, expected_encoded_body)
+ if eol == '\n' or eol == '\r\n':
+ # We know how to split the result back into lines, so maxlinelen
+ # can be checked.
+ for line in encoded_body.splitlines():
+ self.assertLessEqual(len(line), maxlinelen)
+
+ def test_encode_null(self):
+ self._test_encode('', '')
+
+ def test_encode_null_lines(self):
+ self._test_encode('\n\n', '\n\n')
+
+ def test_encode_one_line(self):
+ self._test_encode('hello\n', 'hello\n')
+
+ def test_encode_one_line_crlf(self):
+ self._test_encode('hello\r\n', 'hello\n')
+
+ def test_encode_one_line_eol(self):
+ self._test_encode('hello\n', 'hello\r\n', eol='\r\n')
+
+ def test_encode_one_space(self):
+ self._test_encode(' ', '=20')
+
+ def test_encode_one_line_one_space(self):
+ self._test_encode(' \n', '=20\n')
+
+ def test_encode_one_word_trailing_spaces(self):
+ self._test_encode('hello ', 'hello =20')
+
+ def test_encode_one_line_trailing_spaces(self):
+ self._test_encode('hello \n', 'hello =20\n')
+
+ def test_encode_one_word_trailing_tab(self):
+ self._test_encode('hello \t', 'hello =09')
+
+ def test_encode_one_line_trailing_tab(self):
+ self._test_encode('hello \t\n', 'hello =09\n')
+
+ def test_encode_trailing_space_before_maxlinelen(self):
+ self._test_encode('abcd \n1234', 'abcd =\n\n1234', maxlinelen=6)
+
+ def test_encode_trailing_space_beyond_maxlinelen(self):
+ self._test_encode('abcd \n1234', 'abc=\nd =\n\n1234', maxlinelen=4)
+
+ def test_encode_quoted_equals(self):
+ self._test_encode('a = b', 'a =3D b')
+
+ def test_encode_one_long_string(self):
+ self._test_encode('x' * 100, 'x' * 75 + '=\n' + 'x' * 25)
+
+ def test_encode_one_long_line(self):
+ self._test_encode('x' * 100 + '\n', 'x' * 75 + '=\n' + 'x' * 25 + '\n')
+
+ def test_encode_one_very_long_line(self):
+ self._test_encode('x' * 200 + '\n',
+ 2 * ('x' * 75 + '=\n') + 'x' * 50 + '\n')
+
+ def test_encode_one_long_line(self):
+ self._test_encode('x' * 100 + '\n', 'x' * 75 + '=\n' + 'x' * 25 + '\n')
+
+ def test_encode_shortest_maxlinelen(self):
+ self._test_encode('=' * 5, '=3D=\n' * 4 + '=3D', maxlinelen=4)
def test_encode(self):
eq = self.assertEqual
diff --git a/Lib/random.py b/Lib/random.py
index 592e4b8..49f5859 100644
--- a/Lib/random.py
+++ b/Lib/random.py
@@ -470,6 +470,12 @@ class Random(_random.Random):
Conditions on the parameters are alpha > 0 and beta > 0.
+ The probability distribution function is:
+
+ x ** (alpha - 1) * math.exp(-x / beta)
+ pdf(x) = --------------------------------------
+ math.gamma(alpha) * beta ** alpha
+
"""
# alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2
diff --git a/Lib/test/test_multiprocessing.py b/Lib/test/test_multiprocessing.py
index d1ac4b7..f4031de 100644
--- a/Lib/test/test_multiprocessing.py
+++ b/Lib/test/test_multiprocessing.py
@@ -1084,7 +1084,7 @@ class _TestPool(BaseTestCase):
self.pool.terminate()
join = TimingWrapper(self.pool.join)
join()
- self.assertTrue(join.elapsed < 0.2)
+ self.assertLess(join.elapsed, 0.2)
#
# Test that manager has expected number of shared objects left
#
diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py
index fdbfe19..99d5c70 100644
--- a/Lib/test/test_set.py
+++ b/Lib/test/test_set.py
@@ -1660,6 +1660,39 @@ class TestVariousIteratorArgs(unittest.TestCase):
self.assertRaises(TypeError, getattr(set('january'), methname), N(data))
self.assertRaises(ZeroDivisionError, getattr(set('january'), methname), E(data))
+class bad_eq:
+ def __eq__(self, other):
+ if be_bad:
+ set2.clear()
+ raise ZeroDivisionError
+ return self is other
+ def __hash__(self):
+ return 0
+
+class bad_dict_clear:
+ def __eq__(self, other):
+ if be_bad:
+ dict2.clear()
+ return self is other
+ def __hash__(self):
+ return 0
+
+class TestWeirdBugs(unittest.TestCase):
+ def test_8420_set_merge(self):
+ # This used to segfault
+ global be_bad, set2, dict2
+ be_bad = False
+ set1 = {bad_eq()}
+ set2 = {bad_eq() for i in range(75)}
+ be_bad = True
+ self.assertRaises(ZeroDivisionError, set1.update, set2)
+
+ be_bad = False
+ set1 = {bad_dict_clear()}
+ dict2 = {bad_dict_clear(): None}
+ be_bad = True
+ set1.symmetric_difference_update(dict2)
+
# Application tests (based on David Eppstein's graph recipes ====================================
def powerset(U):
@@ -1804,6 +1837,7 @@ def test_main(verbose=None):
TestIdentities,
TestVariousIteratorArgs,
TestGraphs,
+ TestWeirdBugs,
)
support.run_unittest(*test_classes)
diff --git a/Objects/setobject.c b/Objects/setobject.c
index 30afc7c..7aa1a7f 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -364,12 +364,14 @@ static int
set_add_entry(register PySetObject *so, setentry *entry)
{
register Py_ssize_t n_used;
+ PyObject *key = entry->key;
+ long hash = entry->hash;
assert(so->fill <= so->mask); /* at least one empty slot */
n_used = so->used;
- Py_INCREF(entry->key);
- if (set_insert_key(so, entry->key, (long) entry->hash) == -1) {
- Py_DECREF(entry->key);
+ Py_INCREF(key);
+ if (set_insert_key(so, key, hash) == -1) {
+ Py_DECREF(key);
return -1;
}
if (!(so->used > n_used && so->fill*3 >= (so->mask+1)*2))
@@ -637,6 +639,8 @@ static int
set_merge(PySetObject *so, PyObject *otherset)
{
PySetObject *other;
+ PyObject *key;
+ long hash;
register Py_ssize_t i;
register setentry *entry;
@@ -657,11 +661,13 @@ set_merge(PySetObject *so, PyObject *otherset)
}
for (i = 0; i <= other->mask; i++) {
entry = &other->table[i];
- if (entry->key != NULL &&
- entry->key != dummy) {
- Py_INCREF(entry->key);
- if (set_insert_key(so, entry->key, (long) entry->hash) == -1) {
- Py_DECREF(entry->key);
+ key = entry->key;
+ hash = entry->hash;
+ if (key != NULL &&
+ key != dummy) {
+ Py_INCREF(key);
+ if (set_insert_key(so, key, hash) == -1) {
+ Py_DECREF(key);
return -1;
}
}
@@ -1642,15 +1648,22 @@ set_symmetric_difference_update(PySetObject *so, PyObject *other)
while (_PyDict_Next(other, &pos, &key, &value, &hash)) {
setentry an_entry;
+ Py_INCREF(key);
an_entry.hash = hash;
an_entry.key = key;
+
rv = set_discard_entry(so, &an_entry);
- if (rv == -1)
+ if (rv == -1) {
+ Py_DECREF(key);
return NULL;
+ }
if (rv == DISCARD_NOTFOUND) {
- if (set_add_entry(so, &an_entry) == -1)
+ if (set_add_entry(so, &an_entry) == -1) {
+ Py_DECREF(key);
return NULL;
+ }
}
+ Py_DECREF(key);
}
Py_RETURN_NONE;
}