From c28e1fa71f61278256887d257e4e7e24b0e7e7ce Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Tue, 10 Jun 2008 19:20:26 +0000 Subject: [PATCH] Merged revisions 64002-64003,64012,64036-64037,64047,64050-64052,64054-64055,64066,64071 via svnmerge from svn+ssh://pythondev@svn.python.org/python/trunk ........ r64002 | travis.oliphant | 2008-06-07 00:33:21 +0200 (Sat, 07 Jun 2008) | 1 line Add long double check support to configure test. ........ r64003 | travis.oliphant | 2008-06-07 00:39:47 +0200 (Sat, 07 Jun 2008) | 1 line Remove locking part of new buffer protocol. ........ r64012 | facundo.batista | 2008-06-07 15:36:36 +0200 (Sat, 07 Jun 2008) | 4 lines Finished bug #2451. Fixed the retrying part to make it more robust. ........ r64036 | georg.brandl | 2008-06-08 10:54:40 +0200 (Sun, 08 Jun 2008) | 2 lines #3028: tokenize passes the physical line. ........ r64037 | georg.brandl | 2008-06-08 10:59:38 +0200 (Sun, 08 Jun 2008) | 2 lines Argh, I read it wrong. Reverted 64036 and added a clarifying remark. ........ r64047 | raymond.hettinger | 2008-06-09 03:28:30 +0200 (Mon, 09 Jun 2008) | 1 line Issue3065: Fixed pickling of named tuples. Added tests. ........ r64050 | raymond.hettinger | 2008-06-09 08:54:45 +0200 (Mon, 09 Jun 2008) | 1 line Issue #2138: Add math.factorial(). ........ r64051 | raymond.hettinger | 2008-06-09 10:33:37 +0200 (Mon, 09 Jun 2008) | 1 line Let set.union() and set.update() accept multiple inputs. ........ r64052 | raymond.hettinger | 2008-06-09 11:29:17 +0200 (Mon, 09 Jun 2008) | 1 line Address double-rounding scenarios by setting all variables to long doubles. ........ r64054 | raymond.hettinger | 2008-06-09 13:24:47 +0200 (Mon, 09 Jun 2008) | 1 line Unhappy buildbots. Revert 64052. Long doubles have unexpected effects on some builds. ........ r64055 | raymond.hettinger | 2008-06-09 15:07:27 +0200 (Mon, 09 Jun 2008) | 1 line Let set.intersection() and set.intersection_update() take multiple input arguments. ........ r64066 | robert.schuppenies | 2008-06-10 12:10:31 +0200 (Tue, 10 Jun 2008) | 2 lines Issue 3048: Fixed sys.getsizeof for unicode objects. ........ r64071 | thomas.heller | 2008-06-10 16:07:12 +0200 (Tue, 10 Jun 2008) | 3 lines NEWS entry for: Add an optional 'offset' parameter to byref, defaulting to zero. ........ --- Doc/library/collections.rst | 3 + Doc/library/math.rst | 4 ++ Doc/library/stdtypes.rst | 28 ++++++--- Doc/library/tokenize.rst | 10 ++-- Lib/collections.py | 4 +- Lib/test/test_collections.py | 21 ++++++- Lib/test/test_math.py | 15 +++++ Lib/test/test_set.py | 13 +++++ Lib/test/test_sys.py | 32 ++++++++--- Lib/test/test_urllib2net.py | 24 +++++--- Modules/mathmodule.c | 49 ++++++++++++++++ Objects/setobject.c | 108 ++++++++++++++++++++++++++--------- Objects/unicodeobject.c | 23 ++++++++ 13 files changed, 277 insertions(+), 57 deletions(-) diff --git a/Doc/library/collections.rst b/Doc/library/collections.rst index 5035ac907a5..3203ca584a1 100644 --- a/Doc/library/collections.rst +++ b/Doc/library/collections.rst @@ -519,6 +519,9 @@ Example: if kwds: raise ValueError('Got unexpected field names: %r' % kwds.keys()) return result + + def __getnewargs__(self): + return tuple(self) x = property(itemgetter(0)) y = property(itemgetter(1)) diff --git a/Doc/library/math.rst b/Doc/library/math.rst index 0868e8eb10c..aba6b0e5297 100644 --- a/Doc/library/math.rst +++ b/Doc/library/math.rst @@ -41,6 +41,10 @@ Number-theoretic and representation functions: Return the absolute value of *x*. +.. function:: factorial(x) + + Return *x* factorial. Raises :exc:`ValueError` if *x* is not intergral or + is negative. .. function:: floor(x) diff --git a/Doc/library/stdtypes.rst b/Doc/library/stdtypes.rst index 09549ada39a..27c3fb4160b 100644 --- a/Doc/library/stdtypes.rst +++ b/Doc/library/stdtypes.rst @@ -1501,16 +1501,22 @@ The constructors for both classes work the same: Test whether the set is a true superset of *other*, that is, ``set >= other and set != other``. - .. method:: union(other) - set | other + .. method:: union(other, ...) + set | other | ... Return a new set with elements from both sets. - .. method:: intersection(other) - set & other + .. versionchanged:: 2.6 + Accepts multiple input iterables. + + .. method:: intersection(other, ...) + set & other & ... Return a new set with elements common to both sets. + .. versionchanged:: 2.6 + Accepts multiple input iterables. + .. method:: difference(other) set - other @@ -1562,16 +1568,22 @@ The constructors for both classes work the same: The following table lists operations available for :class:`set` that do not apply to immutable instances of :class:`frozenset`: - .. method:: update(other) - set |= other + .. method:: update(other, ...) + set |= other | ... Update the set, adding elements from *other*. - .. method:: intersection_update(other) - set &= other + .. versionchanged:: 2.6 + Accepts multiple input iterables. + + .. method:: intersection_update(other, ...) + set &= other & ... Update the set, keeping only elements found in it and *other*. + .. versionchanged:: 2.6 + Accepts multiple input iterables. + .. method:: difference_update(other) set -= other diff --git a/Doc/library/tokenize.rst b/Doc/library/tokenize.rst index bbe73d00e9a..b2caded0a7a 100644 --- a/Doc/library/tokenize.rst +++ b/Doc/library/tokenize.rst @@ -1,4 +1,3 @@ - :mod:`tokenize` --- Tokenizer for Python source =============================================== @@ -15,7 +14,6 @@ colorizers for on-screen displays. The primary entry point is a :term:`generator`: - .. function:: tokenize(readline) The :func:`tokenize` generator requires one argument, *readline*, which @@ -28,11 +26,11 @@ The primary entry point is a :term:`generator`: token string; a 2-tuple ``(srow, scol)`` of ints specifying the row and column where the token begins in the source; a 2-tuple ``(erow, ecol)`` of ints specifying the row and column where the token ends in the source; and - the line on which the token was found. The line passed is the *logical* - line; continuation lines are included. + the line on which the token was found. The line passed (the last tuple item) + is the *logical* line; continuation lines are included. - tokenize determines the source encoding of the file by looking for a utf-8 - bom or encoding cookie, according to :pep:`263`. + :func:`tokenize` determines the source encoding of the file by looking for a + UTF-8 BOM or encoding cookie, according to :pep:`263`. All constants from the :mod:`token` module are also exported from diff --git a/Lib/collections.py b/Lib/collections.py index 73fc150e9ab..6d439e52690 100644 --- a/Lib/collections.py +++ b/Lib/collections.py @@ -87,7 +87,9 @@ def namedtuple(typename, field_names, verbose=False): result = self._make(map(kwds.pop, %(field_names)r, self)) if kwds: raise ValueError('Got unexpected field names: %%r' %% kwds.keys()) - return result \n\n''' % locals() + return result \n + def __getnewargs__(self): + return tuple(self) \n\n''' % locals() for i, name in enumerate(field_names): template += ' %s = property(itemgetter(%d))\n' % (name, i) if verbose: diff --git a/Lib/test/test_collections.py b/Lib/test/test_collections.py index 4ed0e241655..9289afe0726 100644 --- a/Lib/test/test_collections.py +++ b/Lib/test/test_collections.py @@ -3,6 +3,7 @@ import unittest, doctest from test import support from collections import namedtuple +import pickle, copy from collections import Hashable, Iterable, Iterator from collections import Sized, Container, Callable from collections import Set, MutableSet @@ -10,6 +11,7 @@ from collections import Mapping, MutableMapping from collections import Sequence, MutableSequence from collections import ByteString +TestNT = namedtuple('TestNT', 'x y z') # type used for pickle tests class TestNamedTuple(unittest.TestCase): @@ -111,7 +113,7 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(Dot(1)._replace(d=999), (999,)) self.assertEqual(Dot(1)._fields, ('d',)) - # n = 10000 + # n = 5000 n = 254 # SyntaxError: more than 255 arguments: import string, random names = list(set(''.join([random.choice(string.ascii_letters) @@ -134,6 +136,23 @@ class TestNamedTuple(unittest.TestCase): self.assertEqual(b2, tuple(b2_expected)) self.assertEqual(b._fields, tuple(names)) + def test_pickle(self): + p = TestNT(x=10, y=20, z=30) + for module in (pickle,): + loads = getattr(module, 'loads') + dumps = getattr(module, 'dumps') + for protocol in -1, 0, 1, 2: + q = loads(dumps(p, protocol)) + self.assertEqual(p, q) + self.assertEqual(p._fields, q._fields) + + def test_copy(self): + p = TestNT(x=10, y=20, z=30) + for copier in copy.copy, copy.deepcopy: + q = copier(p) + self.assertEqual(p, q) + self.assertEqual(p._fields, q._fields) + class TestOneTrickPonyABCs(unittest.TestCase): def test_Hashable(self): diff --git a/Lib/test/test_math.py b/Lib/test/test_math.py index f4c27bb3895..1eafebad222 100644 --- a/Lib/test/test_math.py +++ b/Lib/test/test_math.py @@ -6,6 +6,7 @@ import unittest import math import os import sys +import random eps = 1E-05 NAN = float('nan') @@ -274,6 +275,20 @@ class MathTests(unittest.TestCase): self.ftest('fabs(0)', math.fabs(0), 0) self.ftest('fabs(1)', math.fabs(1), 1) + def testFactorial(self): + def fact(n): + result = 1 + for i in range(1, int(n)+1): + result *= i + return result + values = list(range(10)) + [50, 100, 500] + random.shuffle(values) + for x in range(10): + for cast in (int, float): + self.assertEqual(math.factorial(cast(x)), fact(x), (x, fact(x), math.factorial(x))) + self.assertRaises(ValueError, math.factorial, -1) + self.assertRaises(ValueError, math.factorial, math.pi) + def testFloor(self): self.assertRaises(TypeError, math.floor) self.assertEquals(int, type(math.floor(0.5))) diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py index d05df3f24de..ed2db18e5a4 100644 --- a/Lib/test/test_set.py +++ b/Lib/test/test_set.py @@ -79,6 +79,7 @@ class TestJointOps(unittest.TestCase): self.assertEqual(self.thetype('abcba').union(C('efgfe')), set('abcefg')) self.assertEqual(self.thetype('abcba').union(C('ccb')), set('abc')) self.assertEqual(self.thetype('abcba').union(C('ef')), set('abcef')) + self.assertEqual(self.thetype('abcba').union(C('ef'), C('fg')), set('abcefg')) def test_or(self): i = self.s.union(self.otherword) @@ -103,6 +104,7 @@ class TestJointOps(unittest.TestCase): self.assertEqual(self.thetype('abcba').intersection(C('efgfe')), set('')) self.assertEqual(self.thetype('abcba').intersection(C('ccb')), set('bc')) self.assertEqual(self.thetype('abcba').intersection(C('ef')), set('')) + self.assertEqual(self.thetype('abcba').intersection(C('cbcf'), C('bag')), set('b')) def test_isdisjoint(self): def f(s1, s2): @@ -410,6 +412,12 @@ class TestSet(TestJointOps): s = self.thetype('abcba') self.assertEqual(s.update(C(p)), None) self.assertEqual(s, set(q)) + for p in ('cdc', 'efgfe', 'ccb', 'ef', 'abcda'): + q = 'ahi' + for C in set, frozenset, dict.fromkeys, str, list, tuple: + s = self.thetype('abcba') + self.assertEqual(s.update(C(p), C(q)), None) + self.assertEqual(s, set(s) | set(p) | set(q)) def test_ior(self): self.s |= set(self.otherword) @@ -431,6 +439,11 @@ class TestSet(TestJointOps): s = self.thetype('abcba') self.assertEqual(s.intersection_update(C(p)), None) self.assertEqual(s, set(q)) + ss = 'abcba' + s = self.thetype(ss) + t = 'cbc' + self.assertEqual(s.intersection_update(C(p), C(t)), None) + self.assertEqual(s, set('abcba')&set(p)&set(t)) def test_iand(self): self.s &= set(self.otherword) diff --git a/Lib/test/test_sys.py b/Lib/test/test_sys.py index e9570e90265..39ed0ad8aff 100644 --- a/Lib/test/test_sys.py +++ b/Lib/test/test_sys.py @@ -386,11 +386,14 @@ class SizeofTest(unittest.TestCase): self.file.close() test.support.unlink(test.support.TESTFN) - def check_sizeof(self, o, size): + def check_sizeof(self, o, size, size2=None): + """Check size of o. Possible are size and optionally size2).""" result = sys.getsizeof(o) - msg = 'wrong size for %s: got %d, expected %d' \ - % (type(o), result, size) - self.assertEqual(result, size, msg) + msg = 'wrong size for %s: got %d, expected ' % (type(o), result) + if (size2 != None) and (result != size): + self.assertEqual(result, size2, msg + str(size2)) + else: + self.assertEqual(result, size, msg + str(size)) def align(self, value): mod = value % self.p @@ -486,6 +489,24 @@ class SizeofTest(unittest.TestCase): # list self.check_sizeof([], h + l + p + l) self.check_sizeof([1, 2, 3], h + l + p + l + 3*l) + # unicode + import math + usize = math.log(sys.maxunicode + 1, 2) / 8 + samples = ['', '1'*100] + # we need to test for both sizes, because we don't know if the string + # has been cached + for s in samples: + basicsize = h + l + p + l + l + p + usize * (len(s) + 1) + defenc = bytes(s, 'ascii') + self.check_sizeof(s, basicsize, + size2=basicsize + sys.getsizeof(defenc)) + # trigger caching encoded version as bytes object + try: + getattr(sys, s) + except AttributeError: + pass + finally: + self.check_sizeof(s, basicsize + sys.getsizeof(defenc)) h += l # long @@ -495,9 +516,6 @@ class SizeofTest(unittest.TestCase): self.check_sizeof(32768, h + self.align(2) + 2) self.check_sizeof(32768*32768-1, h + self.align(2) + 2) self.check_sizeof(32768*32768, h + self.align(2) + 4) - # XXX add Unicode support - # self.check_sizeof('', h + l + self.align(i + 1)) - # self.check_sizeof('abc', h + l + self.align(i + 1) + 3) def test_main(): diff --git a/Lib/test/test_urllib2net.py b/Lib/test/test_urllib2net.py index 752406c446b..1c4af18d973 100644 --- a/Lib/test/test_urllib2net.py +++ b/Lib/test/test_urllib2net.py @@ -11,19 +11,25 @@ import os import mimetools -def _urlopen_with_retry(host, *args, **kwargs): - # Connecting to remote hosts is flaky. Make it more robust - # by retrying the connection several times. +def _retry_thrice(func, exc, *args, **kwargs): for i in range(3): try: - return urllib2.urlopen(host, *args, **kwargs) - except urllib2.URLError as e: + return func(*args, **kwargs) + except exc as e: last_exc = e continue except: raise raise last_exc +def _wrap_with_retry_thrice(func, exc): + def wrapped(*args, **kwargs): + return _retry_thrice(func, exc, *args, **kwargs) + return wrapped + +# Connecting to remote hosts is flaky. Make it more robust by retrying +# the connection several times. +_urlopen_with_retry = _wrap_with_retry_thrice(urllib2.urlopen, urllib2.URLError) class AuthTests(unittest.TestCase): @@ -114,7 +120,7 @@ class OtherNetworkTests(unittest.TestCase): 'file:'+sanepathname2url(os.path.abspath(TESTFN)), ('file:///nonsensename/etc/passwd', None, urllib2.URLError), ] - self._test_urls(urls, self._extra_handlers(), urllib2.urlopen) + self._test_urls(urls, self._extra_handlers(), retry=True) finally: os.remove(TESTFN) @@ -146,13 +152,15 @@ class OtherNetworkTests(unittest.TestCase): ## self._test_urls(urls, self._extra_handlers()+[bauth, dauth]) - def _test_urls(self, urls, handlers, urlopen=_urlopen_with_retry): + def _test_urls(self, urls, handlers, retry=True): import socket import time import logging debug = logging.getLogger("test_urllib2").debug - urllib2.install_opener(urllib2.build_opener(*handlers)) + urlopen = urllib2.build_opener(*handlers).open + if retry: + urlopen = _wrap_with_retry_thrice(urlopen, urllib2.URLError) for url in urls: if isinstance(url, tuple): diff --git a/Modules/mathmodule.c b/Modules/mathmodule.c index 16c829ac25c..f759adab5b5 100644 --- a/Modules/mathmodule.c +++ b/Modules/mathmodule.c @@ -567,6 +567,54 @@ PyDoc_STRVAR(math_sum_doc, Return an accurate floating point sum of values in the iterable.\n\ Assumes IEEE-754 floating point arithmetic."); +static PyObject * +math_factorial(PyObject *self, PyObject *arg) +{ + long i, x; + PyObject *result, *iobj, *newresult; + + if (PyFloat_Check(arg)) { + double dx = PyFloat_AS_DOUBLE((PyFloatObject *)arg); + if (dx != floor(dx)) { + PyErr_SetString(PyExc_ValueError, + "factorial() only accepts integral values"); + return NULL; + } + } + + x = PyLong_AsLong(arg); + if (x == -1 && PyErr_Occurred()) + return NULL; + if (x < 0) { + PyErr_SetString(PyExc_ValueError, + "factorial() not defined for negative values"); + return NULL; + } + + result = (PyObject *)PyLong_FromLong(1); + if (result == NULL) + return NULL; + for (i=1 ; i<=x ; i++) { + iobj = (PyObject *)PyLong_FromLong(i); + if (iobj == NULL) + goto error; + newresult = PyNumber_Multiply(result, iobj); + Py_DECREF(iobj); + if (newresult == NULL) + goto error; + Py_DECREF(result); + result = newresult; + } + return result; + +error: + Py_DECREF(result); + Py_XDECREF(iobj); + return NULL; +} + +PyDoc_STRVAR(math_factorial_doc, "Return n!"); + static PyObject * math_trunc(PyObject *self, PyObject *number) { @@ -1022,6 +1070,7 @@ static PyMethodDef math_methods[] = { {"degrees", math_degrees, METH_O, math_degrees_doc}, {"exp", math_exp, METH_O, math_exp_doc}, {"fabs", math_fabs, METH_O, math_fabs_doc}, + {"factorial", math_factorial, METH_O, math_factorial_doc}, {"floor", math_floor, METH_O, math_floor_doc}, {"fmod", math_fmod, METH_VARARGS, math_fmod_doc}, {"frexp", math_frexp, METH_O, math_frexp_doc}, diff --git a/Objects/setobject.c b/Objects/setobject.c index f760b6a94b3..4456ef49d50 100644 --- a/Objects/setobject.c +++ b/Objects/setobject.c @@ -959,15 +959,20 @@ set_update_internal(PySetObject *so, PyObject *other) } static PyObject * -set_update(PySetObject *so, PyObject *other) +set_update(PySetObject *so, PyObject *args) { - if (set_update_internal(so, other) == -1) - return NULL; + Py_ssize_t i; + + for (i=0 ; ilength + 1)); + if (v->defenc) { + defsize = PyObject_CallMethod(v->defenc, "__sizeof__", NULL); + if (defsize == NULL) { + Py_DECREF(res); + return NULL; + } + res = PyNumber_Add(res, defsize); + Py_DECREF(defsize); + } + return res; +} + +PyDoc_STRVAR(sizeof__doc__, +"S.__sizeof__() -> size of S in memory, in bytes"); + static PyObject * unicode_getnewargs(PyUnicodeObject *v) { @@ -8357,6 +8379,7 @@ static PyMethodDef unicode_methods[] = { {"_formatter_parser", (PyCFunction) formatter_parser, METH_NOARGS}, {"maketrans", (PyCFunction) unicode_maketrans, METH_VARARGS | METH_STATIC, maketrans__doc__}, + {"__sizeof__", (PyCFunction) unicode__sizeof__, METH_NOARGS, sizeof__doc__}, #if 0 {"capwords", (PyCFunction) unicode_capwords, METH_NOARGS, capwords__doc__}, #endif