summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Lib/test/test_set.py26
-rw-r--r--Objects/setobject.c28
2 files changed, 50 insertions, 4 deletions
diff --git a/Lib/test/test_set.py b/Lib/test/test_set.py
index 422cc41..dedd1fb 100644
--- a/Lib/test/test_set.py
+++ b/Lib/test/test_set.py
@@ -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
diff --git a/Objects/setobject.c b/Objects/setobject.c
index d33fff9..507a07b 100644
--- a/Objects/setobject.c
+++ b/Objects/setobject.c
@@ -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;
}