Skip to content
Closed
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
Next Next commit
Fix starred tuple equality and pickling
  • Loading branch information
mrahtz committed May 3, 2022
commit 335747dfcab82a0c2f3a2f5137cb24804d823a00
18 changes: 15 additions & 3 deletions Lib/test/test_genericalias.py
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,8 @@ def test_equality(self):
self.assertEqual(list[int], list[int])
self.assertEqual(dict[str, int], dict[str, int])
self.assertEqual((*tuple[int],)[0], (*tuple[int],)[0])
self.assertNotEqual(tuple[int], (*tuple[int],)[0])
self.assertNotEqual(tuple[int], Unpack[tuple[int]])
self.assertEqual(
tuple[
tuple( # Effectively the same as starring; TODO
Expand Down Expand Up @@ -394,6 +396,8 @@ def test_pickle(self):
self.assertEqual(loaded.__origin__, alias.__origin__)
self.assertEqual(loaded.__args__, alias.__args__)
self.assertEqual(loaded.__parameters__, alias.__parameters__)
if isinstance(alias, GenericAlias):
self.assertEqual(loaded.__unpacked__, alias.__unpacked__)

def test_copy(self):
class X(list):
Expand All @@ -419,11 +423,19 @@ def __deepcopy__(self, memo):
self.assertEqual(copied.__parameters__, alias.__parameters__)

def test_unpack(self):
alias = tuple[str, ...]
self.assertIs(alias.__unpacked__, False)
unpacked = (*alias,)[0]
alias1 = tuple[str, ...]
self.assertIs(alias1.__unpacked__, False)
unpacked = (*alias1,)[0]
self.assertIs(unpacked.__unpacked__, True)

# The third positional argument should control unpackedness.
alias2 = GenericAlias(tuple, int)
self.assertIs(alias2.__unpacked__, False)
alias3 = GenericAlias(tuple, int, False)
self.assertIs(alias3.__unpacked__, False)
alias4 = GenericAlias(tuple, int, True)
self.assertIs(alias4.__unpacked__, True)

def test_union(self):
a = typing.Union[list[int], list[str]]
self.assertEqual(a.__args__, (list[int], list[str]))
Expand Down
26 changes: 20 additions & 6 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,9 @@ ga_richcompare(PyObject *a, PyObject *b, int op)

gaobject *aa = (gaobject *)a;
gaobject *bb = (gaobject *)b;
if (aa->starred != bb->starred) {
Py_RETURN_FALSE;
}
int eq = PyObject_RichCompareBool(aa->origin, bb->origin, Py_EQ);
if (eq < 0) {
return NULL;
Expand Down Expand Up @@ -604,8 +607,8 @@ static PyObject *
ga_reduce(PyObject *self, PyObject *Py_UNUSED(ignored))
{
gaobject *alias = (gaobject *)self;
return Py_BuildValue("O(OO)", Py_TYPE(alias),
alias->origin, alias->args);
return Py_BuildValue("O(OOb)", Py_TYPE(alias),
alias->origin, alias->args, alias->starred);
}

static PyObject *
Expand Down Expand Up @@ -685,7 +688,7 @@ static PyGetSetDef ga_properties[] = {
* Returns 1 on success, 0 on failure.
*/
static inline int
setup_ga(gaobject *alias, PyObject *origin, PyObject *args) {
setup_ga(gaobject *alias, PyObject *origin, PyObject *args, bool starred) {
if (!PyTuple_Check(args)) {
args = PyTuple_Pack(1, args);
if (args == NULL) {
Expand All @@ -700,6 +703,7 @@ setup_ga(gaobject *alias, PyObject *origin, PyObject *args) {
alias->origin = origin;
alias->args = args;
alias->parameters = NULL;
alias->starred = starred;
alias->weakreflist = NULL;

if (PyVectorcall_Function(origin) != NULL) {
Expand All @@ -718,16 +722,26 @@ ga_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
if (!_PyArg_NoKeywords("GenericAlias", kwds)) {
return NULL;
}
if (!_PyArg_CheckPositional("GenericAlias", PyTuple_GET_SIZE(args), 2, 2)) {
if (!_PyArg_CheckPositional("GenericAlias", PyTuple_GET_SIZE(args), 2, 3)) {
return NULL;
}

PyObject *origin = PyTuple_GET_ITEM(args, 0);
PyObject *arguments = PyTuple_GET_ITEM(args, 1);

bool starred;
if (PyTuple_Size(args) < 3) {
starred = false;
} else {
PyObject *py_starred = PyTuple_GET_ITEM(args, 2);
starred = PyLong_AsLong(py_starred);
}

gaobject *self = (gaobject *)type->tp_alloc(type, 0);
if (self == NULL) {
return NULL;
}
if (!setup_ga(self, origin, arguments)) {
if (!setup_ga(self, origin, arguments, starred)) {
Py_DECREF(self);
return NULL;
}
Expand Down Expand Up @@ -837,7 +851,7 @@ Py_GenericAlias(PyObject *origin, PyObject *args)
if (alias == NULL) {
return NULL;
}
if (!setup_ga(alias, origin, args)) {
if (!setup_ga(alias, origin, args, false)) {
Py_DECREF(alias);
return NULL;
}
Expand Down