summaryrefslogtreecommitdiffstats
path: root/Python
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2020-06-19 10:16:57 (GMT)
committerGitHub <noreply@github.com>2020-06-19 10:16:57 (GMT)
commit310f6aa7db8dd48952ed718111ce0f016b1c8ef9 (patch)
tree6db8312a4d970336f2fbff579eba7f3abb95bc1e /Python
parent37bb2895561d3e63a631f10875567b4e33b30c07 (diff)
downloadcpython-310f6aa7db8dd48952ed718111ce0f016b1c8ef9.zip
cpython-310f6aa7db8dd48952ed718111ce0f016b1c8ef9.tar.gz
cpython-310f6aa7db8dd48952ed718111ce0f016b1c8ef9.tar.bz2
bpo-40636: PEP 618: add strict parameter to zip() (GH-20921)
zip() now supports PEP 618's strict parameter, which raises a ValueError if the arguments are exhausted at different lengths. Patch by Brandt Bucher. Co-authored-by: Brandt Bucher <brandtbucher@gmail.com> Co-authored-by: Ram Rachum <ram@rachum.com>
Diffstat (limited to 'Python')
-rw-r--r--Python/bltinmodule.c93
1 files changed, 85 insertions, 8 deletions
diff --git a/Python/bltinmodule.c b/Python/bltinmodule.c
index 65f9528..c6ede1c 100644
--- a/Python/bltinmodule.c
+++ b/Python/bltinmodule.c
@@ -2517,9 +2517,10 @@ builtin_issubclass_impl(PyObject *module, PyObject *cls,
typedef struct {
PyObject_HEAD
- Py_ssize_t tuplesize;
- PyObject *ittuple; /* tuple of iterators */
+ Py_ssize_t tuplesize;
+ PyObject *ittuple; /* tuple of iterators */
PyObject *result;
+ int strict;
} zipobject;
static PyObject *
@@ -2530,9 +2531,21 @@ zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
PyObject *ittuple; /* tuple of iterators */
PyObject *result;
Py_ssize_t tuplesize;
+ int strict = 0;
- if (type == &PyZip_Type && !_PyArg_NoKeywords("zip", kwds))
- return NULL;
+ if (kwds) {
+ PyObject *empty = PyTuple_New(0);
+ if (empty == NULL) {
+ return NULL;
+ }
+ static char *kwlist[] = {"strict", NULL};
+ int parsed = PyArg_ParseTupleAndKeywords(
+ empty, kwds, "|$p:zip", kwlist, &strict);
+ Py_DECREF(empty);
+ if (!parsed) {
+ return NULL;
+ }
+ }
/* args must be a tuple */
assert(PyTuple_Check(args));
@@ -2573,6 +2586,7 @@ zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
lz->ittuple = ittuple;
lz->tuplesize = tuplesize;
lz->result = result;
+ lz->strict = strict;
return (PyObject *)lz;
}
@@ -2613,6 +2627,9 @@ zip_next(zipobject *lz)
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) {
Py_DECREF(result);
+ if (lz->strict) {
+ goto check;
+ }
return NULL;
}
olditem = PyTuple_GET_ITEM(result, i);
@@ -2628,28 +2645,85 @@ zip_next(zipobject *lz)
item = (*Py_TYPE(it)->tp_iternext)(it);
if (item == NULL) {
Py_DECREF(result);
+ if (lz->strict) {
+ goto check;
+ }
return NULL;
}
PyTuple_SET_ITEM(result, i, item);
}
}
return result;
+check:
+ if (PyErr_Occurred()) {
+ if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ // next() on argument i raised an exception (not StopIteration)
+ return NULL;
+ }
+ PyErr_Clear();
+ }
+ if (i) {
+ // ValueError: zip() argument 2 is shorter than argument 1
+ // ValueError: zip() argument 3 is shorter than arguments 1-2
+ const char* plural = i == 1 ? " " : "s 1-";
+ return PyErr_Format(PyExc_ValueError,
+ "zip() argument %d is shorter than argument%s%d",
+ i + 1, plural, i);
+ }
+ for (i = 1; i < tuplesize; i++) {
+ it = PyTuple_GET_ITEM(lz->ittuple, i);
+ item = (*Py_TYPE(it)->tp_iternext)(it);
+ if (item) {
+ Py_DECREF(item);
+ const char* plural = i == 1 ? " " : "s 1-";
+ return PyErr_Format(PyExc_ValueError,
+ "zip() argument %d is longer than argument%s%d",
+ i + 1, plural, i);
+ }
+ if (PyErr_Occurred()) {
+ if (!PyErr_ExceptionMatches(PyExc_StopIteration)) {
+ // next() on argument i raised an exception (not StopIteration)
+ return NULL;
+ }
+ PyErr_Clear();
+ }
+ // Argument i is exhausted. So far so good...
+ }
+ // All arguments are exhausted. Success!
+ return NULL;
}
static PyObject *
zip_reduce(zipobject *lz, PyObject *Py_UNUSED(ignored))
{
/* Just recreate the zip with the internal iterator tuple */
- return Py_BuildValue("OO", Py_TYPE(lz), lz->ittuple);
+ if (lz->strict) {
+ return PyTuple_Pack(3, Py_TYPE(lz), lz->ittuple, Py_True);
+ }
+ return PyTuple_Pack(2, Py_TYPE(lz), lz->ittuple);
+}
+
+PyDoc_STRVAR(setstate_doc, "Set state information for unpickling.");
+
+static PyObject *
+zip_setstate(zipobject *lz, PyObject *state)
+{
+ int strict = PyObject_IsTrue(state);
+ if (strict < 0) {
+ return NULL;
+ }
+ lz->strict = strict;
+ Py_RETURN_NONE;
}
static PyMethodDef zip_methods[] = {
{"__reduce__", (PyCFunction)zip_reduce, METH_NOARGS, reduce_doc},
- {NULL, NULL} /* sentinel */
+ {"__setstate__", (PyCFunction)zip_setstate, METH_O, setstate_doc},
+ {NULL} /* sentinel */
};
PyDoc_STRVAR(zip_doc,
-"zip(*iterables) --> A zip object yielding tuples until an input is exhausted.\n\
+"zip(*iterables, strict=False) --> Yield tuples until an input is exhausted.\n\
\n\
>>> list(zip('abcdefg', range(3), range(4)))\n\
[('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]\n\
@@ -2657,7 +2731,10 @@ PyDoc_STRVAR(zip_doc,
The zip object yields n-length tuples, where n is the number of iterables\n\
passed as positional arguments to zip(). The i-th element in every tuple\n\
comes from the i-th iterable argument to zip(). This continues until the\n\
-shortest argument is exhausted.");
+shortest argument is exhausted.\n\
+\n\
+If strict is true and one of the arguments is exhausted before the others,\n\
+raise a ValueError.");
PyTypeObject PyZip_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)