From: <pj...@us...> - 2008-10-30 03:05:36
|
Revision: 5528 http://jython.svn.sourceforge.net/jython/?rev=5528&view=rev Author: pjenvey Date: 2008-10-30 03:05:31 +0000 (Thu, 30 Oct 2008) Log Message: ----------- o make asInt try conversion via custom __int__ methods on non int/longs, as these are valid asInts in most places (like ArgParser.getInt, xrange) o make range use asInt with non long args, which is trickier than it sounds, so rewrote it to work exactly as CPython's -- bonus of being faster for int args Modified Paths: -------------- trunk/jython/Lib/test/test_builtin_jy.py trunk/jython/src/org/python/core/PyObject.java trunk/jython/src/org/python/core/PyString.java trunk/jython/src/org/python/core/PyXRange.java trunk/jython/src/org/python/core/__builtin__.java Modified: trunk/jython/Lib/test/test_builtin_jy.py =================================================================== --- trunk/jython/Lib/test/test_builtin_jy.py 2008-10-30 02:49:44 UTC (rev 5527) +++ trunk/jython/Lib/test/test_builtin_jy.py 2008-10-30 03:05:31 UTC (rev 5528) @@ -102,6 +102,19 @@ return None self.assert_(not callable(Baz())) +class RangeTest(unittest.TestCase): + + class Foo(object): + def __int__(self): + return 3 + foo = Foo() + + def test_range_non_int(self): + self.assertEqual(range(self.foo), [0, 1, 2]) + + def test_xrange_non_int(self): + self.assertEqual(list(xrange(self.foo)), [0, 1, 2]) + def test_main(): test.test_support.run_unittest(BuiltinTest, LoopTest, @@ -110,7 +123,8 @@ ChrTest, ReturnTest, ReprTest, - CallableTest) + CallableTest, + RangeTest) if __name__ == "__main__": test_main() Modified: trunk/jython/src/org/python/core/PyObject.java =================================================================== --- trunk/jython/src/org/python/core/PyObject.java 2008-10-30 02:49:44 UTC (rev 5527) +++ trunk/jython/src/org/python/core/PyObject.java 2008-10-30 03:05:31 UTC (rev 5528) @@ -3866,7 +3866,20 @@ } public int asInt() { - throw Py.TypeError("an integer is required"); + PyObject intObj; + try { + intObj = __int__(); + } catch (PyException pye) { + if (Py.matchException(pye, Py.AttributeError)) { + throw Py.TypeError("an integer is required"); + } + throw pye; + } + if (!(intObj instanceof PyInteger) && !(intObj instanceof PyLong)) { + // Shouldn't happen except with buggy builtin types + throw Py.TypeError("nb_int should return int object"); + } + return intObj.asInt(); } public long asLong(int index) throws ConversionException { Modified: trunk/jython/src/org/python/core/PyString.java =================================================================== --- trunk/jython/src/org/python/core/PyString.java 2008-10-30 02:49:44 UTC (rev 5527) +++ trunk/jython/src/org/python/core/PyString.java 2008-10-30 03:05:31 UTC (rev 5528) @@ -2400,6 +2400,17 @@ return string; } + @Override + public int asInt() { + // We have to override asInt because we override __int__, but generally don't want + // implicit atoi conversions for the base types. blah + PyType type = getType(); + if (type == PyString.TYPE || type == PyUnicode.TYPE || type.lookup("__int__") == null) { + throw Py.TypeError("an integer is required"); + } + return super.asInt(); + } + public String asName(int index) throws PyObject.ConversionException { return internedString(); } Modified: trunk/jython/src/org/python/core/PyXRange.java =================================================================== --- trunk/jython/src/org/python/core/PyXRange.java 2008-10-30 02:49:44 UTC (rev 5527) +++ trunk/jython/src/org/python/core/PyXRange.java 2008-10-30 03:05:31 UTC (rev 5528) @@ -77,7 +77,7 @@ * @param step int value (> 0) * @return int length of range */ - private int getLenOfRange(int lo, int hi, int step) { + static int getLenOfRange(int lo, int hi, int step) { int n = 0; if (lo < hi) { // the base difference may be > Integer.MAX_VALUE Modified: trunk/jython/src/org/python/core/__builtin__.java =================================================================== --- trunk/jython/src/org/python/core/__builtin__.java 2008-10-30 02:49:44 UTC (rev 5527) +++ trunk/jython/src/org/python/core/__builtin__.java 2008-10-30 03:05:31 UTC (rev 5528) @@ -989,48 +989,36 @@ } public static PyObject range(PyObject start, PyObject stop, PyObject step) { - // Check that step is valid. - int stepCmp = step.__cmp__(Py.Zero); - if (stepCmp == -2) { - throw Py.TypeError("non-integer type for step in range()"); - } else if (stepCmp == 0) { - throw Py.ValueError("zero step for range()"); + int ilow = 0; + int ihigh = 0; + int istep = 1; + int n; + + try { + ilow = start.asInt(); + ihigh = stop.asInt(); + istep = step.asInt(); + } catch (PyException pye) { + return handleRangeLongs(start, stop, step); } - // Calculate the number of values in the range. - PyObject n = stop.__sub__(start); - if (n == null) { - throw Py.TypeError("non-integer type for start or stop in range()"); + if (istep == 0) { + throw Py.ValueError("range() step argument must not be zero"); } - n = n.__add__(step); - if (stepCmp == 1) { // step is positive - n = n.__sub__(Py.One).__div__(step); - } else { // step is negative - n = n.__add__(Py.One).__div__(step); + if (istep > 0) { + n = PyXRange.getLenOfRange(ilow, ihigh, istep); + } else { + n = PyXRange.getLenOfRange(ihigh, ilow, -istep); } - - // Check that the number of values is valid. - if (n.__cmp__(Py.Zero) <= 0) { - return new PyList(); + if (n < 0) { + throw Py.OverflowError("range() result has too many items"); } - Object nAsInteger = n.__tojava__(Integer.TYPE); - if (nAsInteger == Py.NoConversion) { - if (n instanceof PyLong) { - throw Py.OverflowError("Can't use range for more than " + Integer.MAX_VALUE + " items. Try xrange instead."); - } else { - throw Py.TypeError("non-integer type for start or stop in range()"); - } - } - // Fill in the range. - int nAsInt = ((Integer) nAsInteger).intValue(); - PyObject j = start; - PyObject[] objs = new PyObject[nAsInt]; - for (int i = 0; i < nAsInt; i++) { - objs[i] = j; - j = j.__add__(step); + PyObject[] range = new PyObject[n]; + for (int i = 0; i < n; i++, ilow += istep) { + range[i] = Py.newInteger(ilow); } - return new PyList(objs); + return new PyList(range); } public static PyObject range(PyObject n) { @@ -1041,6 +1029,70 @@ return range(start, stop, Py.One); } + /** + * Handle range() when PyLong arguments (that OverFlow ints) are given. + */ + private static PyObject handleRangeLongs(PyObject ilow, PyObject ihigh, PyObject istep) { + if (!(ilow instanceof PyInteger) && !(ilow instanceof PyLong)) { + throw Py.TypeError(String.format("range() integer start argument expected, got %s.", + ilow.getType().fastGetName())); + } + if (!(ihigh instanceof PyInteger) && !(ihigh instanceof PyLong)) { + throw Py.TypeError(String.format("range() integer end argument expected, got %s.", + ihigh.getType().fastGetName())); + } + if (!(istep instanceof PyInteger) && !(istep instanceof PyLong)) { + throw Py.TypeError(String.format("range() integer step argument expected, got %s.", + istep.getType().fastGetName())); + } + + int n; + int cmpResult = istep._cmp(Py.Zero); + if (cmpResult == 0) { + throw Py.ValueError("range() step argument must not be zero"); + } + if (cmpResult > 0) { + n = getLenOfRangeLongs(ilow, ihigh, istep); + } else { + n = getLenOfRangeLongs(ihigh, ilow, istep.__neg__()); + } + if (n < 0) { + throw Py.OverflowError("range() result has too many items"); + } + + PyObject[] range = new PyObject[n]; + for (int i = 0; i < n; i++) { + range[i] = ilow.__long__(); + ilow = ilow.__add__(istep); + } + return new PyList(range); + } + + /** + * Return number of items in range (lo, hi, step), when arguments are PyInteger or + * PyLong objects. step > 0 required. Return a value < 0 if & only if the true value + * is too large to fit in an int, or there is an error. + * + * @param lo PyInteger or PyLong value + * @param hi PyInteger or PyLong value + * @param step PyInteger or PyLong value (> 0) + * @return int length of range + */ + private static int getLenOfRangeLongs(PyObject lo, PyObject hi, PyObject step) { + // if (lo >= hi), return length of 0 + if (lo._cmp(hi) >= 0) { + return 0; + } + try { + // See PyXRange.getLenOfRange for the primitive version + PyObject diff = hi.__sub__(lo).__sub__(Py.One); + PyObject n = diff.__floordiv__(step).__add__(Py.One); + return n.asInt(); + } catch (PyException pye) { + return -1; + } + } + private static PyString readline(PyObject file) { if (file instanceof PyFile) { return ((PyFile) file).readline(); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |