For sets with cyclical reprs, emit '...' instead of recursing.
This commit is contained in:
parent
04e820443b
commit
9cdf70399f
|
@ -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
|
||||
|
||||
|
|
|
@ -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().
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue