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):
|
def __cmp__(self, other):
|
||||||
raise RuntimeError
|
raise RuntimeError
|
||||||
|
|
||||||
|
class ReprWrapper:
|
||||||
|
'Used to test self-referential repr() calls'
|
||||||
|
def __repr__(self):
|
||||||
|
return repr(self.value)
|
||||||
|
|
||||||
class TestJointOps(unittest.TestCase):
|
class TestJointOps(unittest.TestCase):
|
||||||
# Tests common to both set and frozenset
|
# 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.discard, BadCmp())
|
||||||
self.assertRaises(RuntimeError, s.remove, 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):
|
class TestSet(TestJointOps):
|
||||||
thetype = set
|
thetype = set
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,9 @@ Core and builtins
|
||||||
custom ``__eq__()`` method to confuse set internals when class instances
|
custom ``__eq__()`` method to confuse set internals when class instances
|
||||||
were used as a set's elements and the ``__eq__()`` method mutated the set.
|
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
|
- Eliminated unnecessary repeated calls to hash() by set.intersection() and
|
||||||
set.symmetric_difference_update().
|
set.symmetric_difference_update().
|
||||||
|
|
||||||
|
|
|
@ -572,34 +572,54 @@ set_tp_print(PySetObject *so, FILE *fp, int flags)
|
||||||
Py_ssize_t pos=0;
|
Py_ssize_t pos=0;
|
||||||
char *emit = ""; /* No separator emitted on first pass */
|
char *emit = ""; /* No separator emitted on first pass */
|
||||||
char *separator = ", ";
|
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);
|
fprintf(fp, "%s([", so->ob_type->tp_name);
|
||||||
while (set_next(so, &pos, &entry)) {
|
while (set_next(so, &pos, &entry)) {
|
||||||
fputs(emit, fp);
|
fputs(emit, fp);
|
||||||
emit = separator;
|
emit = separator;
|
||||||
if (PyObject_Print(entry->key, fp, 0) != 0)
|
if (PyObject_Print(entry->key, fp, 0) != 0) {
|
||||||
|
Py_ReprLeave((PyObject*)so);
|
||||||
return -1;
|
return -1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fputs("])", fp);
|
fputs("])", fp);
|
||||||
|
Py_ReprLeave((PyObject*)so);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
set_repr(PySetObject *so)
|
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);
|
keys = PySequence_List((PyObject *)so);
|
||||||
if (keys == NULL)
|
if (keys == NULL)
|
||||||
return NULL;
|
goto done;
|
||||||
listrepr = PyObject_Repr(keys);
|
listrepr = PyObject_Repr(keys);
|
||||||
Py_DECREF(keys);
|
Py_DECREF(keys);
|
||||||
if (listrepr == NULL)
|
if (listrepr == NULL)
|
||||||
return NULL;
|
goto done;
|
||||||
|
|
||||||
result = PyString_FromFormat("%s(%s)", so->ob_type->tp_name,
|
result = PyString_FromFormat("%s(%s)", so->ob_type->tp_name,
|
||||||
PyString_AS_STRING(listrepr));
|
PyString_AS_STRING(listrepr));
|
||||||
Py_DECREF(listrepr);
|
Py_DECREF(listrepr);
|
||||||
|
done:
|
||||||
|
Py_ReprLeave((PyObject*)so);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue