[Jython-checkins] jython: Run all itertools tests, removing Jython-specific skips

jim.baker jython-checkins at python.org
Tue Dec 9 01:08:38 CET 2014


https://hg.python.org/jython/rev/5ee9b24f3d9d
changeset:   7436:5ee9b24f3d9d
user:        Jim Baker <jim.baker at rackspace.com>
date:        Mon Dec 08 17:08:17 2014 -0700
summary:
  Run all itertools tests, removing Jython-specific skips

Updated count, repeat, and tee in itertools to support various corner
cases seen in usage.

files:
  Lib/test/test_itertools.py                          |  112 ++++-----
  src/org/python/core/__builtin__.java                |    2 +-
  src/org/python/modules/itertools/PyTeeIterator.java |    2 +-
  src/org/python/modules/itertools/count.java         |   92 ++++++-
  src/org/python/modules/itertools/repeat.java        |    5 +
  5 files changed, 132 insertions(+), 81 deletions(-)


diff --git a/Lib/test/test_itertools.py b/Lib/test/test_itertools.py
--- a/Lib/test/test_itertools.py
+++ b/Lib/test/test_itertools.py
@@ -1,5 +1,6 @@
 import unittest
 from test import test_support
+from test.test_weakref import extra_collect
 from itertools import *
 from weakref import proxy
 from decimal import Decimal
@@ -336,11 +337,8 @@
         self.assertEqual(take(2, zip('abc',count(-3))), [('a', -3), ('b', -2)])
         self.assertRaises(TypeError, count, 2, 3, 4)
         self.assertRaises(TypeError, count, 'a')
-
-        #FIXME: not working in Jython
-        #self.assertEqual(list(islice(count(maxsize-5), 10)), range(maxsize-5, maxsize+5))
-        #self.assertEqual(list(islice(count(-maxsize-5), 10)), range(-maxsize-5, -maxsize+5))
-
+        self.assertEqual(list(islice(count(maxsize-5), 10)), range(maxsize-5, maxsize+5))
+        self.assertEqual(list(islice(count(-maxsize-5), 10)), range(-maxsize-5, -maxsize+5))
         c = count(3)
         self.assertEqual(repr(c), 'count(3)')
         c.next()
@@ -348,27 +346,20 @@
         c = count(-9)
         self.assertEqual(repr(c), 'count(-9)')
         c.next()
+        self.assertEqual(repr(count(10.25)), 'count(10.25)')
+        self.assertEqual(c.next(), -8)
+        for i in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 10, sys.maxint-5, sys.maxint+5):
+            # Test repr (ignoring the L in longs)
+            r1 = repr(count(i)).replace('L', '')
+            r2 = 'count(%r)'.__mod__(i).replace('L', '')
+            self.assertEqual(r1, r2)
 
-        #FIXME: not working in Jython
-        #self.assertEqual(repr(count(10.25)), 'count(10.25)')
-        self.assertEqual(c.next(), -8)
-
-        #FIXME: not working in Jython
-        if not test_support.is_jython:
-            for i in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 10, sys.maxint-5, sys.maxint+5):
-                # Test repr (ignoring the L in longs)
-                r1 = repr(count(i)).replace('L', '')
-                r2 = 'count(%r)'.__mod__(i).replace('L', '')
-                self.assertEqual(r1, r2)
-
-        #FIXME: not working in Jython
         # check copy, deepcopy, pickle
-        if not test_support.is_jython:
-            for value in -3, 3, sys.maxint-5, sys.maxint+5:
-                c = count(value)
-                self.assertEqual(next(copy.copy(c)), value)
-                self.assertEqual(next(copy.deepcopy(c)), value)
-                self.assertEqual(next(pickle.loads(pickle.dumps(c))), value)
+        for value in -3, 3, sys.maxint-5, sys.maxint+5:
+            c = count(value)
+            self.assertEqual(next(copy.copy(c)), value)
+            self.assertEqual(next(copy.deepcopy(c)), value)
+            self.assertEqual(next(pickle.loads(pickle.dumps(c))), value)
 
     def test_count_with_stride(self):
         self.assertEqual(zip('abc',count(2,3)), [('a', 2), ('b', 5), ('c', 8)])
@@ -378,17 +369,14 @@
                          [('a', 0), ('b', -1), ('c', -2)])
         self.assertEqual(zip('abc',count(2,0)), [('a', 2), ('b', 2), ('c', 2)])
         self.assertEqual(zip('abc',count(2,1)), [('a', 2), ('b', 3), ('c', 4)])
-
-        #FIXME: not working in Jython
-        #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(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)])
-        #self.assertEqual(repr(take(3, count(10, 2.5))), repr([10, 12.5, 15.0]))
-
+        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(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)])
+        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)')
         c.next()
@@ -402,23 +390,18 @@
         c.next()
         self.assertEqual(repr(c), 'count(-12, -3)')
         self.assertEqual(repr(c), 'count(-12, -3)')
-
-        #FIXME: not working in Jython
-        #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
-
-        #FIXME: not working in Jython
-        if not test_support.is_jython:
-            for i in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 10, sys.maxint-5, sys.maxint+5):
-                for j in  (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 1, 10, sys.maxint-5, sys.maxint+5):
-                    # Test repr (ignoring the L in longs)
-                    r1 = repr(count(i, j)).replace('L', '')
-                    if j == 1:
-                        r2 = ('count(%r)' % i).replace('L', '')
-                    else:
-                        r2 = ('count(%r, %r)' % (i, j)).replace('L', '')
-                    self.assertEqual(r1, r2)
+        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
+        for i in (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 10, sys.maxint-5, sys.maxint+5):
+            for j in  (-sys.maxint-5, -sys.maxint+5 ,-10, -1, 0, 1, 10, sys.maxint-5, sys.maxint+5):
+                # Test repr (ignoring the L in longs)
+                r1 = repr(count(i, j)).replace('L', '')
+                if j == 1:
+                    r2 = ('count(%r)' % i).replace('L', '')
+                else:
+                    r2 = ('count(%r, %r)' % (i, j)).replace('L', '')
+                self.assertEqual(r1, r2)
 
     def test_cycle(self):
         self.assertEqual(take(10, cycle('abc')), list('abcabcabca'))
@@ -918,14 +901,25 @@
         self.assertTrue(list(t1) == list(t2) == list(t3) == list('abc'))
 
         # test that tee objects are weak referencable
-        a, b = tee(xrange(10))
-        p = proxy(a)
-        self.assertEqual(getattr(p, '__class__'), type(b))
-        del a
+        def delocalize():
+            # local variables in Jython cannot be deleted to see
+            # objects go out of scope immediately. Except for tests
+            # like this however this is not going to be observed!
+            a, b = tee(xrange(10))
+            return dict(a=a, b=b)
 
-        #FIXME: not working in Jython
-        if not test_support.is_jython:
-            self.assertRaises(ReferenceError, getattr, p, '__class__')
+        d = delocalize()
+        p = proxy(d['a'])
+        self.assertEqual(getattr(p, '__class__'), type(d['b']))
+        del d['a']
+        extra_collect()  # necessary for Jython to ensure ref queue is cleared out
+        self.assertRaises(ReferenceError, getattr, p, '__class__')
+
+    # Issue 13454: Crash when deleting backward iterator from tee()
+    def test_tee_del_backward(self):
+        forward, backward = tee(repeat(None, 20000000))
+        any(forward)  # exhaust the iterator
+        del backward
 
     def test_StopIteration(self):
         self.assertRaises(StopIteration, izip().next)
diff --git a/src/org/python/core/__builtin__.java b/src/org/python/core/__builtin__.java
--- a/src/org/python/core/__builtin__.java
+++ b/src/org/python/core/__builtin__.java
@@ -972,7 +972,7 @@
         }
         try {
             // See PyXRange.getLenOfRange for the primitive version
-            PyObject diff = hi.__sub__(lo).__sub__(Py.One);
+            PyObject diff = hi._sub(lo)._sub(Py.One);
             PyObject n = diff.__floordiv__(step).__add__(Py.One);
             return n.asInt();
         } catch (PyException pye) {
diff --git a/src/org/python/modules/itertools/PyTeeIterator.java b/src/org/python/modules/itertools/PyTeeIterator.java
--- a/src/org/python/modules/itertools/PyTeeIterator.java
+++ b/src/org/python/modules/itertools/PyTeeIterator.java
@@ -94,7 +94,7 @@
             throw Py.ValueError("n must be >= 0");
         }
 
-        PyObject[] tees = new PyTeeIterator[n];
+        PyObject[] tees = new PyObject[n];
 
         if (n == 0) {
             return tees;
diff --git a/src/org/python/modules/itertools/count.java b/src/org/python/modules/itertools/count.java
--- a/src/org/python/modules/itertools/count.java
+++ b/src/org/python/modules/itertools/count.java
@@ -3,12 +3,14 @@
 
 import org.python.core.ArgParser;
 import org.python.core.Py;
+import org.python.core.PyException;
 import org.python.core.PyInteger;
 import org.python.core.PyIterator;
 import org.python.core.PyObject;
 import org.python.core.PyString;
 import org.python.core.PyTuple;
 import org.python.core.PyType;
+import org.python.core.__builtin__;
 import org.python.expose.ExposedNew;
 import org.python.expose.ExposedMethod;
 import org.python.expose.ExposedType;
@@ -18,8 +20,16 @@
 
     public static final PyType TYPE = PyType.fromClass(count.class);
     private PyIterator iter;
-    private int counter;
-    private int stepper;
+    private PyObject counter;
+    private PyObject stepper;
+
+    private static PyObject NumberClass;
+    private static synchronized PyObject getNumberClass() {
+        if (NumberClass == null) {
+            NumberClass = __builtin__.__import__("numbers").__getattr__("Number");
+        }
+        return NumberClass;
+    }
 
     public static final String count_doc =
         "count(start=0, step=1) --> count object\n\n" +
@@ -37,62 +47,104 @@
     }
 
     /**
-     * Creates an iterator that returns consecutive integers starting at 0.
+     * Creates an iterator that returns consecutive numbers starting at 0.
      */
     public count() {
         super();
-        count___init__(0, 1);
+        count___init__(Py.Zero, Py.One);
     }
 
     /**
-     * Creates an iterator that returns consecutive integers starting at <code>start</code>.
+     * Creates an iterator that returns consecutive numbers starting at <code>start</code>.
      */
-    public count(final int start) {
+    public count(final PyObject start) {
         super();
-        count___init__(start, 1);
+        count___init__(start, Py.One);
     }
 
     /**
-     * Creates an iterator that returns consecutive integers starting at <code>start</code> with <code>step</code> step.
+     * Creates an iterator that returns consecutive numbers starting at <code>start</code> with <code>step</code> step.
      */
-    public count(final int start, final int step) {
+    public count(final PyObject start, final PyObject step) {
         super();
         count___init__(start, step);
     }
 
+    // TODO: move into Py, although NumberClass import time resolution becomes
+    // TODO: a bit trickier
+    private static PyObject getNumber(PyObject obj) {
+        if (Py.isInstance(obj, getNumberClass())) {
+            return obj;
+        }
+        try {
+            PyObject intObj = obj.__int__();
+            if (Py.isInstance(obj, getNumberClass())) {
+                return intObj;
+            }
+            throw Py.TypeError("a number is required");
+        } catch (PyException exc) {
+            if (exc.match(Py.ValueError)) {
+                throw Py.TypeError("a number is required");
+            }
+            throw exc;
+        }
+    }
+
     @ExposedNew
     @ExposedMethod
     final void count___init__(final PyObject[] args, String[] kwds) {
         ArgParser ap = new ArgParser("count", args, kwds, new String[] {"start", "step"}, 0);
-
-        int start = ap.getInt(0, 0);
-        int step = ap.getInt(1, 1);
+        PyObject start = getNumber(ap.getPyObject(0, Py.Zero));
+        PyObject step = getNumber(ap.getPyObject(1, Py.One));
         count___init__(start, step);
     }
 
-    private void count___init__(final int start, final int step) {
+    private void count___init__(final PyObject start, final PyObject step) {
         counter = start;
         stepper = step;
 
         iter = new PyIterator() {
 
             public PyObject __iternext__() {
-                int result = counter;
-                counter += stepper;
-                return new PyInteger(result);
+                PyObject result = counter;
+                counter = counter._add(stepper);
+                return result;
             }
 
         };
     }
 
     @ExposedMethod
+    public PyObject count___copy__() {
+        return new count(counter, stepper);
+    }
+
+    @ExposedMethod
+    final PyObject count___reduce_ex__(PyObject protocol) {
+        return __reduce_ex__(protocol);
+    }
+
+    @ExposedMethod
+    final PyObject count___reduce__() {
+        return __reduce_ex__(Py.Zero);
+    }
+
+
+    public PyObject __reduce_ex__(PyObject protocol) {
+        if (stepper == Py.One) {
+            return new PyTuple(getType(), new PyTuple(counter));
+        } else {
+            return new PyTuple(getType(), new PyTuple(counter, stepper));
+        }
+    }
+
+    @ExposedMethod
     public PyString __repr__() {
-        if (stepper == 1) {
-            return (PyString)(Py.newString("count(%d)").__mod__(Py.newInteger(counter)));
+        if (stepper instanceof PyInteger && stepper._cmp(Py.One) == 0) {
+            return Py.newString(String.format("count(%s)", counter));
         }
         else {
-            return (PyString)(Py.newString("count(%d, %d)").__mod__(new PyTuple(
-                    Py.newInteger(counter), Py.newInteger(stepper))));
+            return Py.newString(String.format("count(%s, %s)", counter, stepper));
         }
     }
 
diff --git a/src/org/python/modules/itertools/repeat.java b/src/org/python/modules/itertools/repeat.java
--- a/src/org/python/modules/itertools/repeat.java
+++ b/src/org/python/modules/itertools/repeat.java
@@ -97,6 +97,11 @@
     }
 
     @ExposedMethod
+    final PyObject __copy__() {
+        return new repeat(object, counter);
+    }
+
+    @ExposedMethod
     public int __len__() {
         if (counter < 0) {
             throw Py.TypeError("object of type 'itertools.repeat' has no len()");

-- 
Repository URL: https://hg.python.org/jython


More information about the Jython-checkins mailing list