10000 bpo-41559: Convert lists to tuples in genericalias to prepare for PEP 612 by Fidget-Spinner · Pull Request #24056 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-41559: Convert lists to tuples in genericalias to prepare for PEP 612 #24056

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 12 commits into from
Closed
Next Next commit
tupleify everything
  • Loading branch information
Fidget-Spinner committed Jan 2, 2021
commit 519b91e716661d64585a3ecc926ac6abcd2d243b
18 changes: 18 additions & 0 deletions Lib/test/test_genericalias.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,5 +391,23 @@ def __call__(self):
self.assertEqual(repr(C1), "collections.abc.Callable"
"[typing.Concatenate[int, ~P], int]")

def test_tupleify(self):
# Converts all lists inside __args__ to tuple.
# For consistency with typing module which ensures
# most types are hashable. Required since PEP 612
# introduces lists during substitution.
< 8000 br>
self.assertEqual(tuple[[]], tuple[(),])
self.assertEqual(tuple[int, [str, dict]], tuple[int, (str, dict)])
self.assertEqual(list[int, [str, [str, dict]]], list[int, (str, (str, dict))])
# Most likely to refleak due to nested list inside tuple.
self.assertEqual(list[int, (str, [str, dict])], list[int, (str, (str, dict))])

x = (int, str, [int, list])
self.assertEqual(list[x], list[int, str, (int, list)])
# Make sure the original tuple wasn't modified.
self.assertEqual(x, (int, str, [int, list]))


if __name__ == "__main__":
unittest.main()
53 changes: 37 additions & 16 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "pycore_object.h"
#include "pycore_unionobject.h" // _Py_union_as_number
#include "structmember.h" // PyMemberDef
#include "pycore_tuple.h" // _PyTuple_FromArray()

typedef struct {
PyObject_HEAD
Expand Down Expand Up @@ -225,6 +226,36 @@ tuple_add(PyObject *self, Py_ssize_t len, PyObject *item)
return 0;
}

// Makes a deep copy of a tuple
static inline PyObject *
tuple_copy(PyObject *obj) {
return _PyTuple_FromArray(((PyTupleObject *)obj)->ob_item, Py_SIZE(obj));
}

// This converts all nested lists in args to a tuple (args should be a tuple).
// Usually needed to help caching in other libraries.
// Must be called on a copy of a tuple object, where the only reference is
// owned by the caller.
static inline void
tupleify_lists(PyObject *args) {
Py_ssize_t len = PyTuple_GET_SIZE(args);
for (Py_ssize_t i = 0; i < len; ++i) {
PyObject *arg = PyTuple_GET_ITEM(args, i);
if (PyList_CheckExact(arg)) {
PyObject *new_arg = PyList_AsTuple(arg);
tupleify_lists(new_arg);
PyTuple_SET_ITEM(args, i, new_arg);
Py_DECREF(arg);
}
else if (PyTuple_CheckExact(arg)) {
// In case there are lists nested inside tuples.
// This works despite interned tuples because tuples containing
// lists aren't interned.
tupleify_lists(arg);
}
}
}

static PyObject *
make_parameters(PyObject *args)
{
Expand Down Expand Up @@ -306,14 +337,7 @@ subs_tvars(PyObject *obj, PyObject *params, PyObject **argitems)
if (iparam >= 0) {
arg = argitems[iparam];
}
// convert all the lists inside args to tuples to help
// with caching in other libaries
if (PyList_CheckExact(arg)) {
arg = PyList_AsTuple(arg);
}
else {
Py_INCREF(arg);
}
Py_INCREF(arg);
PyTuple_SET_ITEM(subargs, i, arg);
}

Expand Down Expand Up @@ -384,13 +408,7 @@ ga_getitem(PyObject *self, PyObject *item)
Py_ssize_t iparam = tuple_index(alias->parameters, nparams, arg);
assert(iparam >= 0);
arg = argitems[iparam];
// convert lists to tuples to help with caching in other libaries.
if (PyList_CheckExact(arg)) {
arg = PyList_AsTuple(arg);
}
else {
Py_INCREF(arg);
}
Py_INCREF(arg);
}
else {
arg = subs_tvars(arg, alias->parameters, argitems);
Expand Down Expand Up @@ -626,10 +644,13 @@ setup_ga(gaobject *alias, PyObject *origin, PyObject *args) {
else {
Py_INCREF(args);
}
PyObject *new_args = tuple_copy(args);
tupleify_lists(new_args);
Py_DECREF(args);

Py_INCREF(origin);
alias->origin = origin;
alias->args = args;
alias->args = new_args;
alias->parameters = NULL;
alias->weakreflist = NULL;
return 1;
Expand Down
0