Teach the UNPACK_SEQUENCE opcode how to tease an iterable object into
giving up the goods. NEEDS DOC CHANGES
This commit is contained in:
parent
2b13ce8317
commit
d6d010b874
|
@ -594,4 +594,57 @@ class TestCase(unittest.TestCase):
|
||||||
except OSError:
|
except OSError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
# Test iterators on RHS of unpacking assignments.
|
||||||
|
def test_unpack_iter(self):
|
||||||
|
a, b = 1, 2
|
||||||
|
self.assertEqual((a, b), (1, 2))
|
||||||
|
|
||||||
|
a, b, c = IteratingSequenceClass(3)
|
||||||
|
self.assertEqual((a, b, c), (0, 1, 2))
|
||||||
|
|
||||||
|
try: # too many values
|
||||||
|
a, b = IteratingSequenceClass(3)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("should have raised ValueError")
|
||||||
|
|
||||||
|
try: # not enough values
|
||||||
|
a, b, c = IteratingSequenceClass(2)
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("should have raised ValueError")
|
||||||
|
|
||||||
|
try: # not iterable
|
||||||
|
a, b, c = len
|
||||||
|
except TypeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
self.fail("should have raised TypeError")
|
||||||
|
|
||||||
|
a, b, c = {1: 42, 2: 42, 3: 42}.itervalues()
|
||||||
|
self.assertEqual((a, b, c), (42, 42, 42))
|
||||||
|
|
||||||
|
f = open(TESTFN, "w")
|
||||||
|
lines = ("a\n", "bb\n", "ccc\n")
|
||||||
|
try:
|
||||||
|
for line in lines:
|
||||||
|
f.write(line)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
f = open(TESTFN, "r")
|
||||||
|
try:
|
||||||
|
a, b, c = f
|
||||||
|
self.assertEqual((a, b, c), lines)
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
try:
|
||||||
|
unlink(TESTFN)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
(a, b), (c,) = IteratingSequenceClass(2), {42: 24}
|
||||||
|
self.assertEqual((a, b, c), (0, 1, 42))
|
||||||
|
|
||||||
run_unittest(TestCase)
|
run_unittest(TestCase)
|
||||||
|
|
|
@ -108,6 +108,8 @@ Core
|
||||||
extend() method of lists
|
extend() method of lists
|
||||||
'x in y' and 'x not in y' (PySequence_Contains() in C API)
|
'x in y' and 'x not in y' (PySequence_Contains() in C API)
|
||||||
operator.countOf() (PySequence_Count() in C API)
|
operator.countOf() (PySequence_Count() in C API)
|
||||||
|
right-hand side of assignment statements with multiple targets, such as
|
||||||
|
x, y, z = some_iterable_object_returning_exactly_3_values
|
||||||
|
|
||||||
- Accessing module attributes is significantly faster (for example,
|
- Accessing module attributes is significantly faster (for example,
|
||||||
random.random or os.path or yourPythonModule.yourAttribute).
|
random.random or os.path or yourPythonModule.yourAttribute).
|
||||||
|
|
|
@ -476,7 +476,7 @@ enum why_code {
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum why_code do_raise(PyObject *, PyObject *, PyObject *);
|
static enum why_code do_raise(PyObject *, PyObject *, PyObject *);
|
||||||
static int unpack_sequence(PyObject *, int, PyObject **);
|
static int unpack_iterable(PyObject *, int, PyObject **);
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
@ -1488,18 +1488,11 @@ eval_frame(PyFrameObject *f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (PySequence_Check(v)) {
|
else if (unpack_iterable(v, oparg,
|
||||||
if (unpack_sequence(v, oparg,
|
stack_pointer + oparg))
|
||||||
stack_pointer + oparg))
|
stack_pointer += oparg;
|
||||||
stack_pointer += oparg;
|
else
|
||||||
else
|
|
||||||
why = WHY_EXCEPTION;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
PyErr_SetString(PyExc_TypeError,
|
|
||||||
"unpack non-sequence");
|
|
||||||
why = WHY_EXCEPTION;
|
why = WHY_EXCEPTION;
|
||||||
}
|
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -2694,37 +2687,50 @@ do_raise(PyObject *type, PyObject *value, PyObject *tb)
|
||||||
return WHY_EXCEPTION;
|
return WHY_EXCEPTION;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Iterate v argcnt times and store the results on the stack (via decreasing
|
||||||
|
sp). Return 1 for success, 0 if error. */
|
||||||
|
|
||||||
static int
|
static int
|
||||||
unpack_sequence(PyObject *v, int argcnt, PyObject **sp)
|
unpack_iterable(PyObject *v, int argcnt, PyObject **sp)
|
||||||
{
|
{
|
||||||
int i;
|
int i = 0;
|
||||||
|
PyObject *it; /* iter(v) */
|
||||||
PyObject *w;
|
PyObject *w;
|
||||||
|
|
||||||
for (i = 0; i < argcnt; i++) {
|
assert(v != NULL);
|
||||||
if (! (w = PySequence_GetItem(v, i))) {
|
|
||||||
if (PyErr_ExceptionMatches(PyExc_IndexError))
|
it = PyObject_GetIter(v);
|
||||||
PyErr_SetString(PyExc_ValueError,
|
if (it == NULL)
|
||||||
"unpack sequence of wrong size");
|
goto Error;
|
||||||
goto finally;
|
|
||||||
|
for (; i < argcnt; i++) {
|
||||||
|
w = PyIter_Next(it);
|
||||||
|
if (w == NULL) {
|
||||||
|
/* Iterator done, via error or exhaustion. */
|
||||||
|
if (!PyErr_Occurred()) {
|
||||||
|
PyErr_Format(PyExc_ValueError,
|
||||||
|
"need more than %d value%s to unpack",
|
||||||
|
i, i == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
goto Error;
|
||||||
}
|
}
|
||||||
*--sp = w;
|
*--sp = w;
|
||||||
}
|
}
|
||||||
/* we better get an IndexError now */
|
|
||||||
if (PySequence_GetItem(v, i) == NULL) {
|
/* We better have exhausted the iterator now. */
|
||||||
if (PyErr_ExceptionMatches(PyExc_IndexError)) {
|
w = PyIter_Next(it);
|
||||||
PyErr_Clear();
|
if (w == NULL) {
|
||||||
return 1;
|
if (PyErr_Occurred())
|
||||||
}
|
goto Error;
|
||||||
/* some other exception occurred. fall through to finally */
|
Py_DECREF(it);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
PyErr_SetString(PyExc_ValueError, "too many values to unpack");
|
||||||
PyErr_SetString(PyExc_ValueError,
|
|
||||||
"unpack sequence of wrong size");
|
|
||||||
/* fall through */
|
/* fall through */
|
||||||
finally:
|
Error:
|
||||||
for (; i > 0; i--, sp++)
|
for (; i > 0; i--, sp++)
|
||||||
Py_DECREF(*sp);
|
Py_DECREF(*sp);
|
||||||
|
Py_XDECREF(it);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue