From 8ddcf3abf7f899369801086bf39f60e0172a1470 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 10 Sep 2016 09:49:24 +0300 Subject: [PATCH] Issue #28019: itertools.count() no longer rounds non-integer step in range between 1.0 and 2.0 to 1. --- Lib/test/test_itertools.py | 28 +++++++++++++++++++----- Misc/NEWS | 3 +++ Modules/itertoolsmodule.c | 45 ++++++++++++++++++++++++-------------- 3 files changed, 55 insertions(+), 21 deletions(-) diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py index f940852ce1b..141791cf9e1 100644 --- a/Lib/test/test_itertools.py +++ b/Lib/test/test_itertools.py @@ -511,12 +511,18 @@ class TestBasicOps(unittest.TestCase): self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)]) self.assertRaises(TypeError, count, 2, 3, 4) self.assertRaises(TypeError, count, 'a') - self.assertEqual(list(islice(count(maxsize-5), 10)), + self.assertEqual(take(10, count(maxsize-5)), list(range(maxsize-5, maxsize+5))) - self.assertEqual(list(islice(count(-maxsize-5), 10)), + self.assertEqual(take(10, count(-maxsize-5)), list(range(-maxsize-5, -maxsize+5))) - self.assertEqual(list(islice(count(10, maxsize+5), 3)), - list(range(10, 10+3*(maxsize+5), maxsize+5))) + self.assertEqual(take(3, count(3.25)), [3.25, 4.25, 5.25]) + self.assertEqual(take(3, count(3.25-4j)), [3.25-4j, 4.25-4j, 5.25-4j]) + self.assertEqual(take(3, count(Decimal('1.1'))), + [Decimal('1.1'), Decimal('2.1'), Decimal('3.1')]) + self.assertEqual(take(3, count(Fraction(2, 3))), + [Fraction(2, 3), Fraction(5, 3), Fraction(8, 3)]) + BIGINT = 1<<1000 + self.assertEqual(take(3, count(BIGINT)), [BIGINT, BIGINT+1, BIGINT+2]) c = count(3) self.assertEqual(repr(c), 'count(3)') next(c) @@ -524,8 +530,10 @@ class TestBasicOps(unittest.TestCase): c = count(-9) self.assertEqual(repr(c), 'count(-9)') next(c) - self.assertEqual(repr(count(10.25)), 'count(10.25)') self.assertEqual(next(c), -8) + self.assertEqual(repr(count(10.25)), 'count(10.25)') + self.assertEqual(repr(count(10.0)), 'count(10.0)') + self.assertEqual(type(next(count(10.0))), float) for i in (-sys.maxsize-5, -sys.maxsize+5 ,-10, -1, 0, 10, sys.maxsize-5, sys.maxsize+5): # Test repr r1 = repr(count(i)) @@ -549,16 +557,22 @@ class TestBasicOps(unittest.TestCase): [('a', 2), ('b', 5), ('c', 8)]) self.assertEqual(lzip('abc',count(step=-1)), [('a', 0), ('b', -1), ('c', -2)]) + self.assertRaises(TypeError, count, 'a', 'b') self.assertEqual(lzip('abc',count(2,0)), [('a', 2), ('b', 2), ('c', 2)]) self.assertEqual(lzip('abc',count(2,1)), [('a', 2), ('b', 3), ('c', 4)]) self.assertEqual(lzip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)]) self.assertEqual(take(20, count(maxsize-15, 3)), take(20, range(maxsize-15, maxsize+100, 3))) self.assertEqual(take(20, count(-maxsize-15, 3)), take(20, range(-maxsize-15,-maxsize+100, 3))) + self.assertEqual(take(3, count(10, maxsize+5)), + list(range(10, 10+3*(maxsize+5), maxsize+5))) + self.assertEqual(take(3, count(2, 1.25)), [2, 3.25, 4.5]) self.assertEqual(take(3, count(2, 3.25-4j)), [2, 5.25-4j, 8.5-8j]) self.assertEqual(take(3, count(Decimal('1.1'), Decimal('.1'))), [Decimal('1.1'), Decimal('1.2'), Decimal('1.3')]) self.assertEqual(take(3, count(Fraction(2,3), Fraction(1,7))), [Fraction(2,3), Fraction(17,21), Fraction(20,21)]) + BIGINT = 1<<1000 + self.assertEqual(take(3, count(step=BIGINT)), [0, BIGINT, 2*BIGINT]) self.assertEqual(repr(take(3, count(10, 2.5))), repr([10, 12.5, 15.0])) c = count(3, 5) self.assertEqual(repr(c), 'count(3, 5)') @@ -576,6 +590,10 @@ class TestBasicOps(unittest.TestCase): self.assertEqual(repr(count(10.5, 1.25)), 'count(10.5, 1.25)') self.assertEqual(repr(count(10.5, 1)), 'count(10.5)') # suppress step=1 when it's an int self.assertEqual(repr(count(10.5, 1.00)), 'count(10.5, 1.0)') # do show float values lilke 1.0 + self.assertEqual(repr(count(10, 1.00)), 'count(10, 1.0)') + c = count(10, 1.0) + self.assertEqual(type(next(c)), int) + self.assertEqual(type(next(c)), float) for i in (-sys.maxsize-5, -sys.maxsize+5 ,-10, -1, 0, 10, sys.maxsize-5, sys.maxsize+5): for j in (-sys.maxsize-5, -sys.maxsize+5 ,-10, -1, 0, 1, 10, sys.maxsize-5, sys.maxsize+5): # Test repr diff --git a/Misc/NEWS b/Misc/NEWS index 443609ab9df..04c9fab2d1c 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -65,6 +65,9 @@ Core and Builtins Library ------- +- Issue #28019: itertools.count() no longer rounds non-integer step in range + between 1.0 and 2.0 to 1. + - Issue #25969: Update the lib2to3 grammar to handle the unpacking generalizations added in 3.5. diff --git a/Modules/itertoolsmodule.c b/Modules/itertoolsmodule.c index 6ff241e7ddc..9c21fb86527 100644 --- a/Modules/itertoolsmodule.c +++ b/Modules/itertoolsmodule.c @@ -3905,7 +3905,7 @@ static PyObject * count_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { countobject *lz; - int slow_mode = 0; + int fast_mode; Py_ssize_t cnt = 0; PyObject *long_cnt = NULL; PyObject *long_step = NULL; @@ -3922,16 +3922,26 @@ count_new(PyTypeObject *type, PyObject *args, PyObject *kwds) return NULL; } + fast_mode = (long_cnt == NULL || PyLong_Check(long_cnt)) && + (long_step == NULL || PyLong_Check(long_step)); + + /* If not specified, start defaults to 0 */ if (long_cnt != NULL) { - cnt = PyLong_AsSsize_t(long_cnt); - if ((cnt == -1 && PyErr_Occurred()) || !PyLong_Check(long_cnt)) { - PyErr_Clear(); - slow_mode = 1; + if (fast_mode) { + assert(PyLong_Check(long_cnt)); + cnt = PyLong_AsSsize_t(long_cnt); + if (cnt == -1 && PyErr_Occurred()) { + PyErr_Clear(); + fast_mode = 0; + } } Py_INCREF(long_cnt); } else { cnt = 0; long_cnt = PyLong_FromLong(0); + if (long_cnt == NULL) { + return NULL; + } } /* If not specified, step defaults to 1 */ @@ -3947,21 +3957,24 @@ count_new(PyTypeObject *type, PyObject *args, PyObject *kwds) assert(long_cnt != NULL && long_step != NULL); /* Fast mode only works when the step is 1 */ - step = PyLong_AsLong(long_step); - if (step != 1) { - slow_mode = 1; - if (step == -1 && PyErr_Occurred()) - PyErr_Clear(); + if (fast_mode) { + assert(PyLong_Check(long_step)); + step = PyLong_AsLong(long_step); + if (step != 1) { + fast_mode = 0; + if (step == -1 && PyErr_Occurred()) + PyErr_Clear(); + } } - if (slow_mode) - cnt = PY_SSIZE_T_MAX; - else + if (fast_mode) Py_CLEAR(long_cnt); + else + cnt = PY_SSIZE_T_MAX; - assert((cnt != PY_SSIZE_T_MAX && long_cnt == NULL && !slow_mode) || - (cnt == PY_SSIZE_T_MAX && long_cnt != NULL && slow_mode)); - assert(slow_mode || + assert((cnt != PY_SSIZE_T_MAX && long_cnt == NULL && fast_mode) || + (cnt == PY_SSIZE_T_MAX && long_cnt != NULL && !fast_mode)); + assert(!fast_mode || (PyLong_Check(long_step) && PyLong_AS_LONG(long_step) == 1)); /* create countobject structure */