]> granicus.if.org Git - python/commitdiff
For sets with cyclical reprs, emit '...' instead of recursing.
authorRaymond Hettinger <python@rcn.com>
Fri, 29 Dec 2006 18:49:13 +0000 (18:49 +0000)
committerRaymond Hettinger <python@rcn.com>
Fri, 29 Dec 2006 18:49:13 +0000 (18:49 +0000)
Lib/test/test_set.py
Misc/NEWS
Objects/setobject.c

index 422cc41c2565c3ba0c86a765a68d156ee4fb29e7..dedd1fb5e599cfdb995df5cd5964f8e4e62f0c16 100644 (file)
@@ -21,6 +21,11 @@ class BadCmp:
     def __cmp__(self, other):
         raise RuntimeError
 
+class ReprWrapper:
+    'Used to test self-referential repr() calls'
+    def __repr__(self):
+        return repr(self.value)
+
 class TestJointOps(unittest.TestCase):
     # Tests common to both set and frozenset
 
@@ -244,6 +249,27 @@ class TestJointOps(unittest.TestCase):
             self.assertRaises(RuntimeError, s.discard, BadCmp())
             self.assertRaises(RuntimeError, s.remove, BadCmp())
 
+    def test_cyclical_repr(self):
+        w = ReprWrapper()
+        s = self.thetype([w])
+        w.value = s
+        name = repr(s).partition('(')[0]    # strip class name from repr string
+        self.assertEqual(repr(s), '%s([%s(...)])' % (name, name))
+
+    def test_cyclical_print(self):
+        w = ReprWrapper()
+        s = self.thetype([w])
+        w.value = s
+        try:
+            fo = open(test_support.TESTFN, "wb")
+            print >> fo, s,
+            fo.close()
+            fo = open(test_support.TESTFN, "rb")
+            self.assertEqual(fo.read(), repr(s))
+        finally:
+            fo.close()
+            os.remove(test_support.TESTFN)
+
 class TestSet(TestJointOps):
     thetype = set
 
index 9acc77194b879260392d0f17c7c7f866b44c82fa..7ab30ed2685d4f589c6566be661d1efbadec8292 100644 (file)
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -18,6 +18,9 @@ Core and builtins
   custom ``__eq__()`` method to confuse set internals when class instances
   were used as a set's elements and the ``__eq__()`` method mutated the set.
 
+- The repr for self-referential sets and fronzensets now shows "..." instead
+  of falling into infinite recursion.
+
 - Eliminated unnecessary repeated calls to hash() by set.intersection() and
   set.symmetric_difference_update().
 
index d33fff93fe247019c1b428192a39a0580a0e03a0..507a07bdcc785617ead5e687f7736b3b3f6b2721 100644 (file)
@@ -572,34 +572,54 @@ set_tp_print(PySetObject *so, FILE *fp, int flags)
        Py_ssize_t pos=0;
        char *emit = "";        /* No separator emitted on first pass */
        char *separator = ", ";
+       int status = Py_ReprEnter((PyObject*)so);
+
+       if (status != 0) {
+               if (status < 0)
+                       return status;
+               fprintf(fp, "%s(...)", so->ob_type->tp_name);
+               return 0;
+       }        
 
        fprintf(fp, "%s([", so->ob_type->tp_name);
        while (set_next(so, &pos, &entry)) {
                fputs(emit, fp);
                emit = separator;
-               if (PyObject_Print(entry->key, fp, 0) != 0)
+               if (PyObject_Print(entry->key, fp, 0) != 0) {
+                       Py_ReprLeave((PyObject*)so);
                        return -1;
+               }
        }
        fputs("])", fp);
+       Py_ReprLeave((PyObject*)so);        
        return 0;
 }
 
 static PyObject *
 set_repr(PySetObject *so)
 {
-       PyObject *keys, *result, *listrepr;
+       PyObject *keys, *result=NULL, *listrepr;
+       int status = Py_ReprEnter((PyObject*)so);
+
+       if (status != 0) {
+               if (status < 0)
+                       return NULL;
+               return PyString_FromFormat("%s(...)", so->ob_type->tp_name);
+       }
 
        keys = PySequence_List((PyObject *)so);
        if (keys == NULL)
-               return NULL;
+               goto done;
        listrepr = PyObject_Repr(keys);
        Py_DECREF(keys);
        if (listrepr == NULL)
-               return NULL;
+               goto done;
 
        result = PyString_FromFormat("%s(%s)", so->ob_type->tp_name,
                PyString_AS_STRING(listrepr));
        Py_DECREF(listrepr);
+done:
+       Py_ReprLeave((PyObject*)so);
        return result;
 }