diff options
-rw-r--r-- | Doc/library/stdtypes.rst | 86 | ||||
-rw-r--r-- | Doc/reference/datamodel.rst | 11 | ||||
-rw-r--r-- | Lib/email/quoprimime.py | 6 | ||||
-rw-r--r-- | Lib/email/test/test_email.py | 207 | ||||
-rw-r--r-- | Lib/random.py | 6 | ||||
-rw-r--r-- | Lib/test/test_multiprocessing.py | 2 | ||||
-rw-r--r-- | Lib/test/test_set.py | 34 | ||||
-rw-r--r-- | Objects/setobject.c | 33 |
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; } |