Commit a26cbb94 authored by Mark Dickinson's avatar Mark Dickinson

Merged revisions 80758 via svnmerge from

svn+ssh://pythondev@svn.python.org/python/trunk

........
  r80758 | mark.dickinson | 2010-05-04 17:18:25 +0100 (Tue, 04 May 2010) | 9 lines

  Issue #1533: fix inconsistency in range function argument processing:
  any non-float non-integer argument is now converted to an integer (if
  possible) using its __int__ method.  Previously, only small arguments
  were treated this way; larger arguments (those whose __int__ was
  outside the range of a C long) would produce a TypeError.

  Patch by Alexander Belopolsky (with minor modifications).
........
parent f2282840
......@@ -3,7 +3,7 @@
import platform
import test.test_support, unittest
from test.test_support import fcmp, have_unicode, TESTFN, unlink, \
run_unittest, run_with_locale
run_unittest, run_with_locale, check_warnings
from operator import neg
import sys, warnings, cStringIO, random, fractions, UserDict
......@@ -1074,6 +1074,10 @@ class BuiltinTest(unittest.TestCase):
# Reject floats when it would require PyLongs to represent.
# (smaller floats still accepted, but deprecated)
self.assertRaises(TypeError, range, 1e100, 1e101, 1e101)
with check_warnings() as w:
warnings.simplefilter("always")
self.assertEqual(range(1.0), [0])
self.assertEqual(w.category, DeprecationWarning)
self.assertRaises(TypeError, range, 0, "spam")
self.assertRaises(TypeError, range, 0, 42, "spam")
......@@ -1081,6 +1085,54 @@ class BuiltinTest(unittest.TestCase):
self.assertRaises(OverflowError, range, -sys.maxint, sys.maxint)
self.assertRaises(OverflowError, range, 0, 2*sys.maxint)
bignum = 2*sys.maxint
smallnum = 42
# Old-style user-defined class with __int__ method
class I0:
def __init__(self, n):
self.n = int(n)
def __int__(self):
return self.n
self.assertEqual(range(I0(bignum), I0(bignum + 1)), [bignum])
self.assertEqual(range(I0(smallnum), I0(smallnum + 1)), [smallnum])
# New-style user-defined class with __int__ method
class I1(object):
def __init__(self, n):
self.n = int(n)
def __int__(self):
return self.n
self.assertEqual(range(I1(bignum), I1(bignum + 1)), [bignum])
self.assertEqual(range(I1(smallnum), I1(smallnum + 1)), [smallnum])
# New-style user-defined class with failing __int__ method
class IX(object):
def __int__(self):
raise RuntimeError
self.assertRaises(RuntimeError, range, IX())
# New-style user-defined class with invalid __int__ method
class IN(object):
def __int__(self):
return "not a number"
self.assertRaises(TypeError, range, IN())
# Exercise various combinations of bad arguments, to check
# refcounting logic
self.assertRaises(TypeError, range, 1e100)
self.assertRaises(TypeError, range, 0, 1e100)
self.assertRaises(TypeError, range, 1e100, 0)
self.assertRaises(TypeError, range, 1e100, 1e100)
self.assertRaises(TypeError, range, 0, 0, 1e100)
self.assertRaises(TypeError, range, 0, 1e100, 1)
self.assertRaises(TypeError, range, 0, 1e100, 1e100)
self.assertRaises(TypeError, range, 1e100, 0, 1)
self.assertRaises(TypeError, range, 1e100, 0, 1e100)
self.assertRaises(TypeError, range, 1e100, 1e100, 1)
self.assertRaises(TypeError, range, 1e100, 1e100, 1e100)
def test_input_and_raw_input(self):
self.write_testfile()
fp = open(TESTFN, 'r')
......
......@@ -12,6 +12,12 @@ What's New in Python 2.6.6 alpha 1?
Core and Builtins
-----------------
- Issue #1533: fix inconsistency in range function argument
processing: any non-float non-integer argument is now converted to
an integer (if possible) using its __int__ method. Previously, only
small arguments were treated this way; larger arguments (those whose
__int__ was outside the range of a C long) would produce a TypeError.
- Issue #8417: Raise an OverflowError when an integer larger than sys.maxsize is
passed to bytearray.
......
......@@ -1745,15 +1745,54 @@ get_len_of_range_longs(PyObject *lo, PyObject *hi, PyObject *step)
return -1;
}
/* Helper function for handle_range_longs. If arg is int or long
object, returns it with incremented reference count. If arg is
float, raises type error. As a last resort, creates a new int by
calling arg type's nb_int method if it is defined. Returns NULL
and sets exception on error.
Returns a new reference to an int object. */
static PyObject *
get_range_long_argument(PyObject *arg, const char *name)
{
PyObject *v;
PyNumberMethods *nb;
if (PyInt_Check(arg) || PyLong_Check(arg)) {
Py_INCREF(arg);
return arg;
}
if (PyFloat_Check(arg) ||
(nb = Py_TYPE(arg)->tp_as_number) == NULL ||
nb->nb_int == NULL) {
PyErr_Format(PyExc_TypeError,
"range() integer %s argument expected, got %s.",
name, arg->ob_type->tp_name);
return NULL;
}
v = nb->nb_int(arg);
if (v == NULL)
return NULL;
if (PyInt_Check(v) || PyLong_Check(v))
return v;
Py_DECREF(v);
PyErr_SetString(PyExc_TypeError,
"__int__ should return int object");
return NULL;
}
/* An extension of builtin_range() that handles the case when PyLong
* arguments are given. */
static PyObject *
handle_range_longs(PyObject *self, PyObject *args)
{
PyObject *ilow;
PyObject *ilow = NULL;
PyObject *ihigh = NULL;
PyObject *istep = NULL;
PyObject *low = NULL;
PyObject *high = NULL;
PyObject *step = NULL;
PyObject *curnum = NULL;
PyObject *v = NULL;
long bign;
......@@ -1772,7 +1811,7 @@ handle_range_longs(PyObject *self, PyObject *args)
/* Figure out which way we were called, supply defaults, and be
* sure to incref everything so that the decrefs at the end
* are correct.
* are correct. NB: ilow, ihigh and istep are borrowed references.
*/
assert(ilow != NULL);
if (ihigh == NULL) {
......@@ -1780,47 +1819,35 @@ handle_range_longs(PyObject *self, PyObject *args)
ihigh = ilow;
ilow = NULL;
}
/* convert ihigh if necessary */
assert(ihigh != NULL);
Py_INCREF(ihigh);
high = get_range_long_argument(ihigh, "end");
if (high == NULL)
goto Fail;
/* ihigh correct now; do ilow */
if (ilow == NULL)
ilow = zero;
Py_INCREF(ilow);
/* ilow and ihigh correct now; do istep */
if (istep == NULL) {
istep = PyLong_FromLong(1L);
if (istep == NULL)
goto Fail;
if (ilow == NULL) {
Py_INCREF(zero);
low = zero;
}
else {
Py_INCREF(istep);
}
if (!PyInt_Check(ilow) && !PyLong_Check(ilow)) {
PyErr_Format(PyExc_TypeError,
"range() integer start argument expected, got %s.",
ilow->ob_type->tp_name);
goto Fail;
low = get_range_long_argument(ilow, "start");
if (low == NULL)
goto Fail;
}
if (!PyInt_Check(ihigh) && !PyLong_Check(ihigh)) {
PyErr_Format(PyExc_TypeError,
"range() integer end argument expected, got %s.",
ihigh->ob_type->tp_name);
/* ilow and ihigh correct now; do istep */
if (istep == NULL)
step = PyLong_FromLong(1);
else
step = get_range_long_argument(istep, "step");
if (step == NULL)
goto Fail;
}
if (!PyInt_Check(istep) && !PyLong_Check(istep)) {
PyErr_Format(PyExc_TypeError,
"range() integer step argument expected, got %s.",
istep->ob_type->tp_name);
if (PyObject_Cmp(step, zero, &cmp_result) == -1)
goto Fail;
}
if (PyObject_Cmp(istep, zero, &cmp_result) == -1)
goto Fail;
if (cmp_result == 0) {
PyErr_SetString(PyExc_ValueError,
"range() step argument must not be zero");
......@@ -1828,13 +1855,13 @@ handle_range_longs(PyObject *self, PyObject *args)
}
if (cmp_result > 0)
bign = get_len_of_range_longs(ilow, ihigh, istep);
bign = get_len_of_range_longs(low, high, step);
else {
PyObject *neg_istep = PyNumber_Negative(istep);
if (neg_istep == NULL)
PyObject *neg_step = PyNumber_Negative(step);
if (neg_step == NULL)
goto Fail;
bign = get_len_of_range_longs(ihigh, ilow, neg_istep);
Py_DECREF(neg_istep);
bign = get_len_of_range_longs(high, low, neg_step);
Py_DECREF(neg_step);
}
n = (int)bign;
......@@ -1848,7 +1875,7 @@ handle_range_longs(PyObject *self, PyObject *args)
if (v == NULL)
goto Fail;
curnum = ilow;
curnum = low;
Py_INCREF(curnum);
for (i = 0; i < n; i++) {
......@@ -1859,24 +1886,24 @@ handle_range_longs(PyObject *self, PyObject *args)
PyList_SET_ITEM(v, i, w);
tmp_num = PyNumber_Add(curnum, istep);
tmp_num = PyNumber_Add(curnum, step);
if (tmp_num == NULL)
goto Fail;
Py_DECREF(curnum);
curnum = tmp_num;
}
Py_DECREF(ilow);
Py_DECREF(ihigh);
Py_DECREF(istep);
Py_DECREF(low);
Py_DECREF(high);
Py_DECREF(step);
Py_DECREF(zero);
Py_DECREF(curnum);
return v;
Fail:
Py_DECREF(ilow);
Py_DECREF(ihigh);
Py_XDECREF(istep);
Py_XDECREF(low);
Py_XDECREF(high);
Py_XDECREF(step);
Py_DECREF(zero);
Py_XDECREF(curnum);
Py_XDECREF(v);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment