Kaydet (Commit) 6912d4dd authored tarafından Tim Peters's avatar Tim Peters

Generalize tuple() to work nicely with iterators.

NEEDS DOC CHANGES.
This one surprised me!  While I expected tuple() to be a no-brainer, turns
out it's actually dripping with consequences:
1. It will *allow* the popular PySequence_Fast() to work with any iterable
   object (code for that not yet checked in, but should be trivial).
2. It caused two std tests to fail.  This because some places used
   PyTuple_Sequence() (the C spelling of tuple()) as an indirect way to test
   whether something *is* a sequence.  But tuple() code only looked for the
   existence of sq->item to determine that, and e.g. an instance passed
   that test whether or not it supported the other operations tuple()
   needed (e.g., __len__).  So some things the tests *expected* to fail
   with an AttributeError now fail with a TypeError instead.  This looks
   like an improvement to me; e.g., test_coercion used to produce 559
   TypeErrors and 2 AttributeErrors, and now they're all TypeErrors.  The
   error details are more informative too, because the places calling this
   were *looking* for TypeErrors in order to replace the generic tuple()
   "not a sequence" msg with their own more specific text, and
   AttributeErrors snuck by that.
üst f4848dac
......@@ -911,7 +911,7 @@ xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx*/
tuple or list. Use PySequence_Fast_GET_ITEM to access the
members of this list.
Returns NULL on failure. If the object is not a sequence,
Returns NULL on failure. If the object does not support iteration,
raises a TypeError exception with m as the message text.
*/
......
......@@ -516,7 +516,7 @@ test_coercion
[1] % None ... exceptions.TypeError
[1] %= None ... exceptions.TypeError
[1] + <MethodNumber 1> ... exceptions.TypeError
[1] += <MethodNumber 1> ... exceptions.AttributeError
[1] += <MethodNumber 1> ... exceptions.TypeError
[1] - <MethodNumber 1> ... exceptions.TypeError
[1] -= <MethodNumber 1> ... exceptions.TypeError
[1] * <MethodNumber 1> = [1]
......@@ -528,7 +528,7 @@ test_coercion
[1] % <MethodNumber 1> ... exceptions.TypeError
[1] %= <MethodNumber 1> ... exceptions.TypeError
[1] + <CoerceNumber 2> ... exceptions.TypeError
[1] += <CoerceNumber 2> ... exceptions.AttributeError
[1] += <CoerceNumber 2> ... exceptions.TypeError
[1] - <CoerceNumber 2> ... exceptions.TypeError
[1] -= <CoerceNumber 2> ... exceptions.TypeError
[1] * <CoerceNumber 2> = [1, 1]
......
......@@ -58,20 +58,20 @@ g(1, 2, 3, *(4, 5))
class Nothing: pass
try:
g(*Nothing())
except AttributeError, attr:
except TypeError, attr:
pass
else:
print "should raise AttributeError: __len__"
print "should raise TypeError"
class Nothing:
def __len__(self):
return 5
try:
g(*Nothing())
except AttributeError, attr:
except TypeError, attr:
pass
else:
print "should raise AttributeError: __getitem__"
print "should raise TypeError"
class Nothing:
def __len__(self):
......
......@@ -275,6 +275,39 @@ class TestCase(unittest.TestCase):
except OSError:
pass
# Test tuples()'s use of iterators.
def test_builtin_tuple(self):
self.assertEqual(tuple(SequenceClass(5)), (0, 1, 2, 3, 4))
self.assertEqual(tuple(SequenceClass(0)), ())
self.assertEqual(tuple([]), ())
self.assertEqual(tuple(()), ())
self.assertEqual(tuple("abc"), ("a", "b", "c"))
d = {"one": 1, "two": 2, "three": 3}
self.assertEqual(tuple(d), tuple(d.keys()))
self.assertRaises(TypeError, tuple, list)
self.assertRaises(TypeError, tuple, 42)
f = open(TESTFN, "w")
try:
for i in range(5):
f.write("%d\n" % i)
finally:
f.close()
f = open(TESTFN, "r")
try:
self.assertEqual(tuple(f), ("0\n", "1\n", "2\n", "3\n", "4\n"))
f.seek(0, 0)
self.assertEqual(tuple(f.xreadlines()),
("0\n", "1\n", "2\n", "3\n", "4\n"))
finally:
f.close()
try:
unlink(TESTFN)
except OSError:
pass
# Test filter()'s use of iterators.
def test_builtin_filter(self):
self.assertEqual(filter(None, SequenceClass(5)), range(1, 5))
......
......@@ -24,9 +24,9 @@ Core
min()
reduce()
XXX TODO string.join(), unicode.join()
XXX TODO tuple()
tuple()
XXX TODO zip()
XXX TODO 'x in y' (!) (?)
XXX TODO 'x in y'
What's New in Python 2.1 (final)?
=================================
......
......@@ -1176,61 +1176,68 @@ PySequence_DelSlice(PyObject *s, int i1, int i2)
PyObject *
PySequence_Tuple(PyObject *v)
{
PySequenceMethods *m;
PyObject *it; /* iter(v) */
int n; /* guess for result tuple size */
PyObject *result;
int j;
if (v == NULL)
return null_error();
/* Special-case the common tuple and list cases, for efficiency. */
if (PyTuple_Check(v)) {
Py_INCREF(v);
return v;
}
if (PyList_Check(v))
return PyList_AsTuple(v);
/* There used to be code for strings here, but tuplifying strings is
not a common activity, so I nuked it. Down with code bloat! */
/* Get iterator. */
it = PyObject_GetIter(v);
if (it == NULL)
return type_error("tuple() argument must support iteration");
/* Generic sequence object */
m = v->ob_type->tp_as_sequence;
if (m && m->sq_item) {
int i;
PyObject *t;
int n = PySequence_Size(v);
if (n < 0)
return NULL;
t = PyTuple_New(n);
if (t == NULL)
return NULL;
for (i = 0; ; i++) {
PyObject *item = (*m->sq_item)(v, i);
if (item == NULL) {
if (PyErr_ExceptionMatches(PyExc_IndexError))
PyErr_Clear();
else {
Py_DECREF(t);
t = NULL;
}
break;
}
if (i >= n) {
if (n < 500)
n += 10;
else
n += 100;
if (_PyTuple_Resize(&t, n, 0) != 0)
break;
}
PyTuple_SET_ITEM(t, i, item);
/* Guess result size and allocate space. */
n = PySequence_Size(v);
if (n < 0) {
PyErr_Clear();
n = 10; /* arbitrary */
}
result = PyTuple_New(n);
if (result == NULL)
goto Fail;
/* Fill the tuple. */
for (j = 0; ; ++j) {
PyObject *item = PyIter_Next(it);
if (item == NULL) {
if (PyErr_Occurred())
goto Fail;
break;
}
if (j >= n) {
if (n < 500)
n += 10;
else
n += 100;
if (_PyTuple_Resize(&result, n, 0) != 0)
goto Fail;
}
if (i < n && t != NULL)
_PyTuple_Resize(&t, i, 0);
return t;
PyTuple_SET_ITEM(result, j, item);
}
/* None of the above */
return type_error("tuple() argument must be a sequence");
/* Cut tuple back if guess was too large. */
if (j < n &&
_PyTuple_Resize(&result, j, 0) != 0)
goto Fail;
Py_DECREF(it);
return result;
Fail:
Py_XDECREF(result);
Py_DECREF(it);
return NULL;
}
PyObject *
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment