Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
The C implementation.
  • Loading branch information
serhiy-storchaka committed May 29, 2022
commit c638e0ed0164843f8debb34b21d9b404f0073d03
19 changes: 8 additions & 11 deletions Lib/test/test_typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -758,28 +758,25 @@ class C(Generic[*Ts]): pass
('generic[*Ts]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...]]'),
('generic[*Ts]', '[str, *tuple_type[int, ...], bool]', 'generic[str, *tuple_type[int, ...], bool]'),

# Technically, multiple unpackings are forbidden by PEP 646, but we
# choose to be less restrictive at runtime, to allow folks room
# to experiment. So all three of these should be valid.
#('generic[*Ts]', '[*tuple_type[int, ...], *tuple_type[str, ...]]', 'generic[*tuple_type[int, ...], *tuple_type[str, ...]]'),

('generic[*Ts]', '[*Ts]', 'generic[*Ts]'),
('generic[*Ts]', '[T, *Ts]', 'generic[T, *Ts]'),
('generic[*Ts]', '[*Ts, T]', 'generic[*Ts, T]'),
('generic[T, *Ts]', '[int]', 'generic[int]'),
('generic[T, *Ts]', '[int, str]', 'generic[int, str]'),
('generic[T, *Ts]', '[int, str, bool]', 'generic[int, str, bool]'),

#('generic[T, *Ts]', '[*tuple[int, ...]]', 'TypeError'), # Should be generic[int, *tuple[int, ...]]
('C[T, *Ts]', '[*tuple_type[int, ...]]', 'C[int, *tuple_type[int, ...]]'),
('C[*Ts, T]', '[*tuple_type[int, ...]]', 'C[*tuple_type[int, ...], int]'),
('C[T1, *Ts, T2]', '[*tuple_type[int, ...]]', 'C[int, *tuple_type[int, ...], int]'),


('generic[*Ts, T]', '[int]', 'generic[int]'),
('generic[*Ts, T]', '[int, str]', 'generic[int, str]'),
('generic[*Ts, T]', '[int, str, bool]', 'generic[int, str, bool]'),

('generic[T, *Ts]', '[*tuple_type[int, ...]]', 'generic[int, *tuple_type[int, ...]]'),
('generic[*Ts, T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...], int]'),
('generic[T1, *Ts, T2]', '[*tuple_type[int, ...]]', 'generic[int, *tuple_type[int, ...], int]'),
('generic[T, str, *Ts]', '[*tuple_type[int, ...]]', 'generic[int, str, *tuple_type[int, ...]]'),
('generic[*Ts, str, T]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...], str, int]'),
('generic[list[T], *Ts]', '[*tuple_type[int, ...]]', 'generic[list[int], *tuple_type[int, ...]]'),
('generic[*Ts, list[T]]', '[*tuple_type[int, ...]]', 'generic[*tuple_type[int, ...], list[int]]'),

('generic[T, *tuple_type[int, ...]]', '[str]', 'generic[str, *tuple_type[int, ...]]'),
('generic[T1, T2, *tuple_type[int, ...]]', '[str, bool]', 'generic[str, bool, *tuple_type[int, ...]]'),
('generic[T1, *tuple_type[int, ...], T2]', '[str, bool]', 'generic[str, *tuple_type[int, ...], bool]'),
Expand Down
20 changes: 10 additions & 10 deletions Lib/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1429,8 +1429,8 @@ def _determine_new_args(self, args):
alen = len(args)
plen = len(params)
if typevartuple_index is not None:
i = typevartuple_index
j = plen - typevartuple_index - 1
left = typevartuple_index
right = plen - typevartuple_index - 1
var_tuple_index = None
for k, arg in enumerate(args):
if _is_unpacked_var_tuple(arg):
Expand All @@ -1439,19 +1439,19 @@ def _determine_new_args(self, args):
var_tuple_index = k
fillarg = args[var_tuple_index].__typing_unpacked_tuple_args__[0]
if var_tuple_index is not None:
i = min(i, var_tuple_index)
j = min(j, alen - var_tuple_index - 1)
elif i + j > alen:
left = min(left, var_tuple_index)
right = min(right, alen - var_tuple_index - 1)
elif left + right > alen:
raise TypeError(f"Too few arguments for {self};"
f" actual {alen}, expected at least {plen-1}")

new_arg_by_param.update(zip(params[:i], args[:i]))
for k in range(i, typevartuple_index):
new_arg_by_param.update(zip(params[:left], args[:left]))
for k in range(left, typevartuple_index):
new_arg_by_param[params[k]] = fillarg
new_arg_by_param[params[typevartuple_index]] = tuple(args[i: alen - j])
for k in range(typevartuple_index + 1, plen - j):
new_arg_by_param[params[typevartuple_index]] = tuple(args[left: alen - right])
for k in range(typevartuple_index + 1, plen - right):
new_arg_by_param[params[k]] = fillarg
new_arg_by_param.update(zip(params[plen - j:], args[alen - j:]))
new_arg_by_param.update(zip(params[plen - right:], args[alen - right:]))
else:
if alen != plen:
raise TypeError(f"Too {'many' if alen > plen else 'few'} arguments for {self};"
Expand Down
96 changes: 86 additions & 10 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,9 @@ _Py_make_parameters(PyObject *args)
a non-empty tuple, return a new reference to obj. */
static PyObject *
subs_tvars(PyObject *obj, PyObject *params,
PyObject **argitems, Py_ssize_t nargs, Py_ssize_t varparam)
PyObject **argitems, Py_ssize_t nargs,
Py_ssize_t varparam, Py_ssize_t left, Py_ssize_t right,
PyObject *fillarg)
{
PyObject *subparams;
if (_PyObject_LookupAttr(obj, &_Py_ID(__parameters__), &subparams) < 0) {
Expand All @@ -283,28 +285,36 @@ subs_tvars(PyObject *obj, PyObject *params,
Py_DECREF(subparams);
return NULL;
}
for (Py_ssize_t i = 0, j = 0; i < nsubargs; ++i) {
Py_ssize_t j = 0;
for (Py_ssize_t i = 0; i < nsubargs; ++i) {
PyObject *arg = PyTuple_GET_ITEM(subparams, i);
Py_ssize_t iparam = tuple_index(params, nparams, arg);
if (iparam == varparam) {
j = tuple_extend(&subargs, j,
argitems + iparam, nargs - nparams + 1);
argitems + left, nargs - left - right);
if (j < 0) {
return NULL;
}
}
else {
if (iparam >= 0) {
if (iparam > varparam) {
iparam += nargs - nsubargs;
if (iparam < left) {
arg = argitems[iparam];
}
else if (iparam >= nparams - right) {
iparam += nargs - nparams;
arg = argitems[iparam];
}
else {
arg = fillarg;
}
arg = argitems[iparam];
}
Py_INCREF(arg);
PyTuple_SET_ITEM(subargs, j, arg);
j++;
}
}
assert(j == PyTuple_GET_SIZE(subargs));

obj = PyObject_GetItem(obj, subargs);

Expand Down Expand Up @@ -399,6 +409,27 @@ _unpack_args(PyObject *item)
return newargs;
}

static PyObject *
_get_unpacked_var_tuple_arg(PyObject *arg)
{
if (PyType_Check(arg)) {
return NULL;
}
PyObject *subargs = _unpacked_tuple_args(arg);
if (subargs != NULL &&
PyTuple_Check(subargs) &&
PyTuple_GET_SIZE(subargs) == 2 &&
PyTuple_GET_ITEM(subargs, 1) == Py_Ellipsis)
{
PyObject *subarg = PyTuple_GET_ITEM(subargs, 0);
Py_INCREF(subarg);
Py_DECREF(subargs);
return subarg;
}
Py_XDECREF(subargs);
return NULL;
}

PyObject *
_Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObject *item)
{
Expand All @@ -425,8 +456,37 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
varparam = i;
}
}
PyObject *fillarg = NULL;
Py_ssize_t vartuplearg = nitems;
Py_ssize_t left = varparam;
Py_ssize_t right = nparams - varparam - 1;
if (varparam < nparams) {
if (nitems < nparams - 1) {
for (Py_ssize_t i = 0; i < nitems; i++) {
PyObject *arg = _get_unpacked_var_tuple_arg(argitems[i]);
if (arg) {
if (vartuplearg < nitems) {
Py_DECREF(arg);
Py_DECREF(fillarg);
Py_DECREF(item);
return PyErr_Format(PyExc_TypeError,
"More than one unpacked variable-size tuple argument",
self);
}
vartuplearg = i;
fillarg = arg;
}
else if (PyErr_Occurred()) {
Py_XDECREF(fillarg);
Py_DECREF(item);
return NULL;
}
}
if (vartuplearg < nitems) {
assert(fillarg);
left = Py_MIN(left, vartuplearg);
right = Py_MIN(right, nitems - vartuplearg - 1);
}
else if (left + right > nitems) {
Py_DECREF(item);
return PyErr_Format(PyExc_TypeError,
"Too few arguments for %R",
Expand All @@ -451,6 +511,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
PyObject *newargs = PyTuple_New(nargs);
if (newargs == NULL) {
Py_XDECREF(fillarg);
Py_DECREF(item);
return NULL;
}
Expand All @@ -459,12 +520,14 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
int unpack = _is_unpacked_typevartuple(arg);
if (unpack < 0) {
Py_DECREF(newargs);
Py_XDECREF(fillarg);
Py_DECREF(item);
return NULL;
}
PyObject *subst;
if (_PyObject_LookupAttr(arg, &_Py_ID(__typing_subst__), &subst) < 0) {
Py_DECREF(newargs);
Py_XDECREF(fillarg);
Py_DECREF(item);
return NULL;
}
Expand All @@ -474,22 +537,33 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
if (iparam == varparam) {
Py_DECREF(subst);
Py_DECREF(newargs);
Py_XDECREF(fillarg);
Py_DECREF(item);
PyErr_SetString(PyExc_TypeError,
"Substitution of bare TypeVarTuple is not supported");
return NULL;
}
if (iparam > varparam) {
if (iparam < left) {
arg = argitems[iparam];
}
else if (iparam >= nparams - right) {
iparam += nitems - nparams;
arg = argitems[iparam];
}
else {
assert(fillarg);
arg = fillarg;
}
arg = PyObject_CallOneArg(subst, argitems[iparam]);
arg = PyObject_CallOneArg(subst, arg);
Py_DECREF(subst);
}
else {
arg = subs_tvars(arg, parameters, argitems, nitems, varparam);
arg = subs_tvars(arg, parameters, argitems, nitems,
varparam, left, right, fillarg);
}
if (arg == NULL) {
Py_DECREF(newargs);
Py_XDECREF(fillarg);
Py_DECREF(item);
return NULL;
}
Expand All @@ -498,6 +572,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
&PyTuple_GET_ITEM(arg, 0), PyTuple_GET_SIZE(arg));
Py_DECREF(arg);
if (jarg < 0) {
Py_XDECREF(fillarg);
Py_DECREF(item);
return NULL;
}
Expand All @@ -508,6 +583,7 @@ _Py_subs_parameters(PyObject *self, PyObject *args, PyObject *parameters, PyObje
}
}

Py_XDECREF(fillarg);
Py_DECREF(item);
return newargs;
}
Expand Down