Issue #28019: itertools.count() no longer rounds non-integer step in range

between 1.0 and 2.0 to 1.
This commit is contained in:
Serhiy Storchaka 2016-09-10 09:53:51 +03:00
commit 8f9cafad3d
3 changed files with 55 additions and 21 deletions

View File

@ -510,12 +510,18 @@ class TestBasicOps(unittest.TestCase):
self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)]) self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)])
self.assertRaises(TypeError, count, 2, 3, 4) self.assertRaises(TypeError, count, 2, 3, 4)
self.assertRaises(TypeError, count, 'a') 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))) 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))) list(range(-maxsize-5, -maxsize+5)))
self.assertEqual(list(islice(count(10, maxsize+5), 3)), self.assertEqual(take(3, count(3.25)), [3.25, 4.25, 5.25])
list(range(10, 10+3*(maxsize+5), maxsize+5))) 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) c = count(3)
self.assertEqual(repr(c), 'count(3)') self.assertEqual(repr(c), 'count(3)')
next(c) next(c)
@ -523,8 +529,10 @@ class TestBasicOps(unittest.TestCase):
c = count(-9) c = count(-9)
self.assertEqual(repr(c), 'count(-9)') self.assertEqual(repr(c), 'count(-9)')
next(c) next(c)
self.assertEqual(repr(count(10.25)), 'count(10.25)')
self.assertEqual(next(c), -8) 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): for i in (-sys.maxsize-5, -sys.maxsize+5 ,-10, -1, 0, 10, sys.maxsize-5, sys.maxsize+5):
# Test repr # Test repr
r1 = repr(count(i)) r1 = repr(count(i))
@ -548,16 +556,22 @@ class TestBasicOps(unittest.TestCase):
[('a', 2), ('b', 5), ('c', 8)]) [('a', 2), ('b', 5), ('c', 8)])
self.assertEqual(lzip('abc',count(step=-1)), self.assertEqual(lzip('abc',count(step=-1)),
[('a', 0), ('b', -1), ('c', -2)]) [('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,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,1)), [('a', 2), ('b', 3), ('c', 4)])
self.assertEqual(lzip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)]) 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(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(2, 3.25-4j)), [2, 5.25-4j, 8.5-8j])
self.assertEqual(take(3, count(Decimal('1.1'), Decimal('.1'))), self.assertEqual(take(3, count(Decimal('1.1'), Decimal('.1'))),
[Decimal('1.1'), Decimal('1.2'), Decimal('1.3')]) [Decimal('1.1'), Decimal('1.2'), Decimal('1.3')])
self.assertEqual(take(3, count(Fraction(2,3), Fraction(1,7))), self.assertEqual(take(3, count(Fraction(2,3), Fraction(1,7))),
[Fraction(2,3), Fraction(17,21), Fraction(20,21)]) [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])) self.assertEqual(repr(take(3, count(10, 2.5))), repr([10, 12.5, 15.0]))
c = count(3, 5) c = count(3, 5)
self.assertEqual(repr(c), 'count(3, 5)') self.assertEqual(repr(c), 'count(3, 5)')
@ -575,6 +589,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.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)), '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.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 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): for j in (-sys.maxsize-5, -sys.maxsize+5 ,-10, -1, 0, 1, 10, sys.maxsize-5, sys.maxsize+5):
# Test repr # Test repr

View File

@ -135,6 +135,9 @@ Core and Builtins
Library Library
------- -------
- Issue #28019: itertools.count() no longer rounds non-integer step in range
between 1.0 and 2.0 to 1.
- Issue #18401: Pdb now supports the 'readrc' keyword argument to control - Issue #18401: Pdb now supports the 'readrc' keyword argument to control
whether .pdbrc files should be read. Patch by Martin Matusiak and whether .pdbrc files should be read. Patch by Martin Matusiak and
Sam Kimbrel. Sam Kimbrel.

View File

@ -3907,7 +3907,7 @@ static PyObject *
count_new(PyTypeObject *type, PyObject *args, PyObject *kwds) count_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{ {
countobject *lz; countobject *lz;
int slow_mode = 0; int fast_mode;
Py_ssize_t cnt = 0; Py_ssize_t cnt = 0;
PyObject *long_cnt = NULL; PyObject *long_cnt = NULL;
PyObject *long_step = NULL; PyObject *long_step = NULL;
@ -3924,16 +3924,26 @@ count_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL; 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) { if (long_cnt != NULL) {
cnt = PyLong_AsSsize_t(long_cnt); if (fast_mode) {
if ((cnt == -1 && PyErr_Occurred()) || !PyLong_Check(long_cnt)) { assert(PyLong_Check(long_cnt));
PyErr_Clear(); cnt = PyLong_AsSsize_t(long_cnt);
slow_mode = 1; if (cnt == -1 && PyErr_Occurred()) {
PyErr_Clear();
fast_mode = 0;
}
} }
Py_INCREF(long_cnt); Py_INCREF(long_cnt);
} else { } else {
cnt = 0; cnt = 0;
long_cnt = PyLong_FromLong(0); long_cnt = PyLong_FromLong(0);
if (long_cnt == NULL) {
return NULL;
}
} }
/* If not specified, step defaults to 1 */ /* If not specified, step defaults to 1 */
@ -3949,21 +3959,24 @@ count_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
assert(long_cnt != NULL && long_step != NULL); assert(long_cnt != NULL && long_step != NULL);
/* Fast mode only works when the step is 1 */ /* Fast mode only works when the step is 1 */
step = PyLong_AsLong(long_step); if (fast_mode) {
if (step != 1) { assert(PyLong_Check(long_step));
slow_mode = 1; step = PyLong_AsLong(long_step);
if (step == -1 && PyErr_Occurred()) if (step != 1) {
PyErr_Clear(); fast_mode = 0;
if (step == -1 && PyErr_Occurred())
PyErr_Clear();
}
} }
if (slow_mode) if (fast_mode)
cnt = PY_SSIZE_T_MAX;
else
Py_CLEAR(long_cnt); Py_CLEAR(long_cnt);
else
cnt = PY_SSIZE_T_MAX;
assert((cnt != PY_SSIZE_T_MAX && long_cnt == NULL && !slow_mode) || assert((cnt != PY_SSIZE_T_MAX && long_cnt == NULL && fast_mode) ||
(cnt == PY_SSIZE_T_MAX && long_cnt != NULL && slow_mode)); (cnt == PY_SSIZE_T_MAX && long_cnt != NULL && !fast_mode));
assert(slow_mode || assert(!fast_mode ||
(PyLong_Check(long_step) && PyLong_AS_LONG(long_step) == 1)); (PyLong_Check(long_step) && PyLong_AS_LONG(long_step) == 1));
/* create countobject structure */ /* create countobject structure */