Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 30 additions & 1 deletion Lib/test/clinic.test.c
Original file line number Diff line number Diff line change
Expand Up @@ -5220,6 +5220,7 @@ Test___init___impl(TestObj *self, PyObject *args)
/*[clinic end generated code: output=0e5836c40dbc2397 input=a615a4485c0fc3e2]*/

/*[clinic input]
@vectorcall
@classmethod
Test.__new__
*args: tuple
Expand Down Expand Up @@ -5261,9 +5262,37 @@ Test(PyTypeObject *type, PyObject *args, PyObject *kwargs)
return return_value;
}

static PyObject *
Test_vectorcall(PyObject *type, PyObject *const *args,
size_t nargsf, PyObject *kwnames)
{
PyObject *return_value = NULL;
Py_ssize_t nargs = PyVectorcall_NARGS(nargsf);
PyObject *__clinic_args = NULL;

assert(_PyType_CAST(type)->tp_new == Test);
/* Make sure the type object is immutable: the generated
* vectorcall doesn't deal e.g. with users reassigning __init__. */
assert(PyType_HasFeature(_PyType_CAST(type), Py_TPFLAGS_IMMUTABLETYPE));
if (!_PyArg_NoKwnames("Test", kwnames)) {
goto exit;
}
__clinic_args = PyTuple_FromArray(args, nargs);
if (__clinic_args == NULL) {
goto exit;
}
return_value = Test_impl(_PyType_CAST(type), __clinic_args);

exit:
/* Cleanup for args */
Py_XDECREF(__clinic_args);

return return_value;
}

static PyObject *
Test_impl(PyTypeObject *type, PyObject *args)
/*[clinic end generated code: output=e6fba0c8951882fd input=8ce30adb836aeacb]*/
/*[clinic end generated code: output=07f68e3971ed24cc input=1dc824fd6cec5f8e]*/


/*[clinic input]
Expand Down
79 changes: 35 additions & 44 deletions Python/bltinmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -3139,41 +3139,46 @@ typedef struct {

#define _zipobject_CAST(op) ((zipobject *)(op))

/*[clinic input]
class zip "zipobject *" "&PyZip_Type"
@vectorcall
@classmethod
zip.__new__ as zip_new

*iterables: array
strict: bool = False

Iterate over several iterables in parallel, yielding tuples.
Comment thread
cmaloney marked this conversation as resolved.
Resolved

The zip object yields n-length tuples, where n is the number of
iterables passed as positional arguments to zip(). The i-th element
in every tuple comes from the i-th iterable argument to zip(). This
continues until the shortest argument is exhausted.

If strict is true and one of the arguments is exhausted before the
others, raise a ValueError.

>>> list(zip('abcdefg', range(3), range(4)))
[('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]
[clinic start generated code]*/

static PyObject *
zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
zip_new_impl(PyTypeObject *type, PyObject * const *iterables,
Py_ssize_t iterables_length, int strict)
/*[clinic end generated code: output=5e1a30829ce1f6ab input=4bd8e6a24e4bfb2e]*/

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change

{
zipobject *lz;
Py_ssize_t i;
PyObject *ittuple; /* tuple of iterators */
PyObject *result;
Py_ssize_t tuplesize;
int strict = 0;

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));
tuplesize = PyTuple_GET_SIZE(args);

/* obtain iterators */
ittuple = PyTuple_New(tuplesize);
ittuple = PyTuple_New(iterables_length);
if (ittuple == NULL)
return NULL;
for (i=0; i < tuplesize; ++i) {
PyObject *item = PyTuple_GET_ITEM(args, i);
PyObject *it = PyObject_GetIter(item);
for (i=0; i < iterables_length; ++i) {
PyObject *it = PyObject_GetIter(iterables[i]);
if (it == NULL) {
Py_DECREF(ittuple);
return NULL;
Expand All @@ -3182,12 +3187,12 @@ zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
}

/* create a result holder */
result = PyTuple_New(tuplesize);
result = PyTuple_New(iterables_length);
if (result == NULL) {
Py_DECREF(ittuple);
return NULL;
}
for (i=0 ; i < tuplesize ; i++) {
for (i=0 ; i < iterables_length ; i++) {
PyTuple_SET_ITEM(result, i, Py_NewRef(Py_None));
}

Expand All @@ -3199,7 +3204,7 @@ zip_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return NULL;
}
lz->ittuple = ittuple;
lz->tuplesize = tuplesize;
lz->tuplesize = iterables_length;
lz->result = result;
lz->strict = strict;

Expand Down Expand Up @@ -3345,21 +3350,6 @@ static PyMethodDef zip_methods[] = {
{NULL} /* sentinel */
};

PyDoc_STRVAR(zip_doc,
"zip(*iterables, strict=False)\n\
--\n\
\n\
The zip object yields n-length tuples, where n is the number of\n\
iterables passed as positional arguments to zip(). The i-th element\n\
in every tuple comes from the i-th iterable argument to zip(). This\n\
continues until the shortest argument is exhausted.\n\
\n\
If strict is true and one of the arguments is exhausted before the\n\
others, raise a ValueError.\n\
\n\
>>> list(zip('abcdefg', range(3), range(4)))\n\
[('a', 0, 0), ('b', 1, 1), ('c', 2, 2)]");

PyTypeObject PyZip_Type = {
PyVarObject_HEAD_INIT(&PyType_Type, 0)
"zip", /* tp_name */
Expand All @@ -3383,7 +3373,7 @@ PyTypeObject PyZip_Type = {
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
Py_TPFLAGS_BASETYPE, /* tp_flags */
zip_doc, /* tp_doc */
zip_new__doc__, /* tp_doc */
zip_traverse, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
Expand All @@ -3402,6 +3392,7 @@ PyTypeObject PyZip_Type = {
PyType_GenericAlloc, /* tp_alloc */
zip_new, /* tp_new */
PyObject_GC_Del, /* tp_free */
.tp_vectorcall = zip_vectorcall,
};


Expand Down
107 changes: 106 additions & 1 deletion Python/clinic/bltinmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 18 additions & 8 deletions Tools/clinic/libclinic/parse_args.py
Original file line number Diff line number Diff line change
Expand Up @@ -923,14 +923,14 @@ def render_option_groups_parse_tuple(
parser_code.append(" }}")
return parser_code

def _parse_vararg(self) -> str:
def _parse_vararg(self, *, fastcall: bool) -> str:
assert self.varpos is not None
c = self.varpos.converter
assert isinstance(c, libclinic.converters.VarPosCConverter)
return c.parse_vararg(pos_only=self.pos_only,
min_pos=self.min_pos,
max_pos=self.max_pos,
fastcall=self.fastcall,
fastcall=fastcall,
limited_capi=self.limited_capi)

def _parse_kwarg(self) -> str:
Expand Down Expand Up @@ -963,6 +963,7 @@ def _parse_positional_args(
argname_fmt: str,
nargs: str,
limited_capi: bool,
fastcall: bool,
) -> list[str] | None:
"""Emit per-parameter positional argument parsing.

Expand Down Expand Up @@ -995,8 +996,8 @@ def _parse_positional_args(
if self.min_pos < len(self.parameters):
parser_code.append("skip_optional:")
if self.varpos:
parser_code.append(libclinic.normalize_snippet(self._parse_vararg(),
indent=4))
parser_code.append(libclinic.normalize_snippet(
self._parse_vararg(fastcall=fastcall), indent=4))
elif self.var_keyword:
parser_code.append(libclinic.normalize_snippet(self._parse_kwarg(),
indent=4))
Expand Down Expand Up @@ -1090,7 +1091,7 @@ def parse_pos_only(self) -> None:

pos_code = self._parse_positional_args(
argname_fmt=argname_fmt, nargs=nargs,
limited_capi=self.limited_capi)
limited_capi=self.limited_capi, fastcall=self.fastcall)
if pos_code is not None:
parser_code.extend(pos_code)
else:
Expand Down Expand Up @@ -1148,7 +1149,8 @@ def parse_var_keyword(self) -> None:
parser_code.append("skip_optional:")

if self.varpos:
parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4))
parser_code.append(libclinic.normalize_snippet(
self._parse_vararg(fastcall=self.fastcall), indent=4))
if self.var_keyword:
parser_code.append(libclinic.normalize_snippet(self._parse_kwarg(), indent=4))
self.parser_body(*parser_code)
Expand Down Expand Up @@ -1376,7 +1378,12 @@ def parse_general(self, clang: CLanguage) -> None:
if add_label:
parser_code.append("%s:" % add_label)
if self.varpos:
parser_code.append(libclinic.normalize_snippet(self._parse_vararg(), indent=4))
# The vectorcall helper gets an array of arguments, not a
# tuple: use the converters' fastcall convention, which
# handles that.
fastcall = True if self.func.vectorcall else self.fastcall
parser_code.append(libclinic.normalize_snippet(
self._parse_vararg(fastcall=fastcall), indent=4))
else:
self.use_converters()

Expand Down Expand Up @@ -1617,8 +1624,11 @@ def _vectorcall_positional(self, *,

arity_checked: Already have a number of arguments check.
"""
# The vectorcall gets an array of arguments, not a tuple: use the
# converters' fastcall convention, which handles that.
pos_code = self._parse_positional_args(
argname_fmt='args[%d]', nargs='nargs', limited_capi=False)
argname_fmt='args[%d]', nargs='nargs', limited_capi=False,
fastcall=True)
# Converter support was validated when @vectorcall was parsed.
assert pos_code is not None
if arity_checked:
Expand Down
Loading