[pypy-svn] pypy default: (fijal, arigo)

arigo commits-noreply at bitbucket.org
Thu Jan 20 10:25:53 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r40965:ee1f82a06937
Date: 2011-01-19 19:40 +0100
http://bitbucket.org/pypy/pypy/changeset/ee1f82a06937/

Log:	(fijal, arigo)

	Start refactoring range().

diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -11,22 +11,11 @@
 from pypy.interpreter.argument import Arguments
 from pypy.rlib.rarithmetic import r_uint, intmask
 from pypy.rlib.objectmodel import specialize
-from pypy.module.__builtin__.app_functional import range as app_range
 from inspect import getsource, getfile
 from pypy.rlib.jit import unroll_safe
 
-"""
-Implementation of the common integer case of range. Instead of handling
-all other cases here, too, we fall back to the applevel implementation
-for non-integer arguments.
-Ideally this implementation could be saved, if we were able to
-specialize the geninterp generated code. But I guess having this
-hand-optimized is a good idea.
 
-Note the fun of using range inside range :-)
-"""
-
-def get_len_of_range(lo, hi, step):
+def get_len_of_range(space, lo, hi, step):
     """
     Return number of items in range/xrange (lo, hi, step).
     Raise ValueError if step == 0 and OverflowError if the true value is too
@@ -44,7 +33,8 @@
     # hi-lo-1 = M-(-M-1)-1 = 2*M.  Therefore unsigned long has enough
     # precision to compute the RHS exactly.
     if step == 0:
-        raise ValueError
+        raise OperationError(space.w_ValueError,
+                             space.wrap("step argument must not be zero"))
     elif step < 0:
         lo, hi, step = hi, lo, -step
     if lo < hi:
@@ -53,27 +43,48 @@
         diff = uhi - ulo - 1
         n = intmask(diff // r_uint(step) + 1)
         if n < 0:
-            raise OverflowError
+            raise OperationError(space.w_OverflowError,
+                                 space.wrap("result has too many items"))
     else:
         n = 0
     return n
 
-def range(space, w_x, w_y=None, w_step=1):
+def range_int(space, w_x, w_y=NoneNotWrapped, w_step=1):
     """Return a list of integers in arithmetic position from start (defaults
 to zero) to stop - 1 by step (defaults to 1).  Use a negative step to
 get a list in decending order."""
 
+    if w_y is None:
+        w_start = space.wrap(0)
+        w_stop = w_x
+    else:
+        w_start = w_x
+        w_stop = w_y
+
+    if space.is_true(space.isinstance(w_stop, space.w_float)):
+        raise OperationError(space.w_TypeError,
+            space.wrap("range() integer end argument expected, got float."))
+    if space.is_true(space.isinstance(w_start, space.w_float)):
+        raise OperationError(space.w_TypeError,
+            space.wrap("range() integer start argument expected, got float."))
+    if space.is_true(space.isinstance(w_step, space.w_float)):
+        raise OperationError(space.w_TypeError,
+            space.wrap("range() integer step argument expected, got float."))
+
+    w_start = space.int(w_start)
+    w_stop  = space.int(w_stop)
+    w_step  = space.int(w_step)
+
     try:
-        x = space.int_w(space.int(w_x))
-        if space.is_w(w_y, space.w_None):
-            start, stop = 0, x
-        else:
-            start, stop = x, space.int_w(space.int(w_y))
-        step = space.int_w(space.int(w_step))
-        howmany = get_len_of_range(start, stop, step)
-    except (ValueError, OverflowError, OperationError):
-        # save duplication by redirecting every error to applevel
-        return range_fallback(space, w_x, w_y, w_step)
+        start = space.int_w(w_start)
+        stop  = space.int_w(w_stop)
+        step  = space.int_w(w_step)
+    except OperationError, e:
+        if not e.match(space, space.w_OverflowError):
+            raise
+        return range_with_longs(space, w_start, w_stop, w_step)
+
+    howmany = get_len_of_range(space, start, stop, step)
 
     if space.config.objspace.std.withrangelist:
         return range_withspecialized_implementation(space, start,
@@ -84,12 +95,8 @@
         res_w[idx] = space.wrap(v)
         v += step
     return space.newlist(res_w)
-range_int = range
 range_int.unwrap_spec = [ObjSpace, W_Root, W_Root, W_Root]
-del range # don't hide the builtin one
 
-range_fallback = applevel(getsource(app_range), getfile(app_range)
-                          ).interphook('range')
 
 def range_withspecialized_implementation(space, start, step, howmany):
     assert space.config.objspace.std.withrangelist
@@ -585,15 +592,7 @@
             start, stop = 0, start
         else:
             stop = _toint(space, w_stop)
-        try:
-            howmany = get_len_of_range(start, stop, step)
-        except ValueError:
-            raise OperationError(space.w_ValueError,
-                                 space.wrap("xrange() arg 3 must not be zero"))
-        except OverflowError:
-            raise OperationError(space.w_OverflowError,
-                                 space.wrap("xrange() result has "
-                                            "too many items"))
+        howmany = get_len_of_range(space, start, stop, step)
         obj = space.allocate_instance(W_XRange, w_subtype)
         W_XRange.__init__(obj, space, start, howmany, step)
         return space.wrap(obj)

diff --git a/pypy/module/__builtin__/__init__.py b/pypy/module/__builtin__/__init__.py
--- a/pypy/module/__builtin__/__init__.py
+++ b/pypy/module/__builtin__/__init__.py
@@ -30,9 +30,6 @@
         'print'         : 'app_io.print_',
 
         'apply'         : 'app_functional.apply',
-        #'range'         : 'app_functional.range',
-        # redirected to functional.py, applevel version
-        # is still needed and should stay where it is.
         'sorted'        : 'app_functional.sorted',
         'vars'          : 'app_inspect.vars',
         'dir'           : 'app_inspect.dir',

diff --git a/pypy/module/__builtin__/test/test_range.py b/pypy/module/__builtin__/test/test_range.py
--- a/pypy/module/__builtin__/test/test_range.py
+++ b/pypy/module/__builtin__/test/test_range.py
@@ -60,8 +60,12 @@
        raises(ValueError, range, 1, 5, 0)
 
    def test_range_float(self):
-       "How CPython does it - UGLY."
-       assert range(0.1, 2.0, 1.1) == [0, 1]
+       raises(TypeError, range, 0.1)
+       raises(TypeError, range, 0.1, 0)
+       raises(TypeError, range, 0, 0.1)
+       raises(TypeError, range, 0.1, 0, 0)
+       raises(TypeError, range, 0, 0.1, 0)
+       raises(TypeError, range, 0, 0, 0.1)
 
    def test_range_wrong_type(self):
        raises(TypeError, range, "42")
@@ -83,10 +87,7 @@
        assert range(0, 2**100, -1) == []
 
        a = long(10 * sys.maxint)
-       b = long(100 * sys.maxint)
-       c = long(50 * sys.maxint)
-
        assert range(a, a+2) == [a, a+1]
        assert range(a+2, a, -1L) == [a+2, a+1]
        assert range(a+4, a, -2) == [a+4, a+2]
- 
+       assert range(a, a*5, a) == [a, 2*a, 3*a, 4*a]

diff --git a/pypy/module/__builtin__/app_functional.py b/pypy/module/__builtin__/app_functional.py
--- a/pypy/module/__builtin__/app_functional.py
+++ b/pypy/module/__builtin__/app_functional.py
@@ -11,66 +11,8 @@
 
 # ____________________________________________________________
 
-"""
-The following is a nice example of collaboration between
-interp-level and app-level.
-range is primarily implemented in functional.py for the integer case.
-On every error or different data types, it redirects to the applevel
-implementation below. functional.py uses this source via the inspect
-module and uses gateway.applevel. This is also an alternative to
-writing longer functions in strings.
-"""
-
-def range(x, y=None, step=1):
-    """ returns a list of integers in arithmetic position from start (defaults
-        to zero) to stop - 1 by step (defaults to 1).  Use a negative step to
-        get a list in decending order."""
-
-
-    if y is None: 
-        start = 0
-        stop = x
-    else:
-        start = x
-        stop = y
-
-    if not isinstance(start, (int, long)):
-        raise TypeError('range() integer start argument expected, got %s' % type(start))
-    if not isinstance(stop, (int, long)):
-        raise TypeError('range() integer stop argument expected, got %s' % type(stop))
-    if not isinstance(step, (int, long)):
-        raise TypeError('range() integer step argument expected, got %s' % type(step))
-
-    if step == 0:
-        raise ValueError, 'range() arg 3 must not be zero'
-
-    elif step > 0:
-        if stop <= start: # no work for us
-            return []
-        howmany = (stop - start + step - 1)/step
-
-    else:  # step must be < 0, or we would have raised ValueError
-        if stop >= start: # no work for us
-            return []
-        howmany = (start - stop - step  - 1)/-step
-
-    arr = [None] * howmany  # this is to avoid using append.
-
-    i = start
-    n = 0
-    while n < howmany:
-        arr[n] = i
-        i += step
-        n += 1
-
-    return arr
-
-# ____________________________________________________________
-
 def sorted(lst, cmp=None, key=None, reverse=None):
     "sorted(iterable, cmp=None, key=None, reverse=False) --> new sorted list"
     sorted_lst = list(lst)
     sorted_lst.sort(cmp, key, reverse)
     return sorted_lst
-
-


More information about the Pypy-commit mailing list