Skip to content

Commit

Permalink
Add tests for using PEP560 with classes implemented in C. (#4883)
Browse files Browse the repository at this point in the history
Based on tests from #4878
  • Loading branch information
serhiy-storchaka authored and ilevkivskyi committed Dec 16, 2017
1 parent 9c19b02 commit 45700fb
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Lib/test/test_genericclass.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import unittest
from test import support


class TestMROEntry(unittest.TestCase):
Expand Down Expand Up @@ -248,5 +249,22 @@ def __class_getitem__(cls, item):
self.assertEqual(C[int], 'from metaclass')


@support.cpython_only
class CAPITest(unittest.TestCase):

def test_c_class(self):
from _testcapi import Generic, GenericAlias
self.assertIsInstance(Generic.__class_getitem__(Generic, int), GenericAlias)

IntGeneric = Generic[int]
self.assertIs(type(IntGeneric), GenericAlias)
self.assertEqual(IntGeneric.__mro_entries__(()), (int,))
class C(IntGeneric):
pass
self.assertEqual(C.__bases__, (int,))
self.assertEqual(C.__orig_bases__, (IntGeneric,))
self.assertEqual(C.__mro__, (C, int, object))


if __name__ == "__main__":
unittest.main()
85 changes: 85 additions & 0 deletions Modules/_testcapimodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -5053,6 +5053,81 @@ recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds)
}


/* Test PEP 560 */

typedef struct {
PyObject_HEAD
PyObject *item;
} PyGenericAliasObject;

static void
generic_alias_dealloc(PyGenericAliasObject *self)
{
Py_CLEAR(self->item);
}

static PyObject *
generic_alias_mro_entries(PyGenericAliasObject *self, PyObject *bases)
{
return PyTuple_Pack(1, self->item);
}

static PyMethodDef generic_alias_methods[] = {
{"__mro_entries__", (PyCFunction) generic_alias_mro_entries, METH_O, NULL},
{NULL} /* sentinel */
};

PyTypeObject GenericAlias_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"GenericAlias",
sizeof(PyGenericAliasObject),
0,
.tp_dealloc = (destructor)generic_alias_dealloc,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_methods = generic_alias_methods,
};

static PyObject *
generic_alias_new(PyObject *item)
{
PyGenericAliasObject *o = PyObject_New(PyGenericAliasObject, &GenericAlias_Type);
if (o == NULL) {
return NULL;
}
Py_INCREF(item);
o->item = item;
return (PyObject*) o;
}

typedef struct {
PyObject_HEAD
} PyGenericObject;

static PyObject *
generic_class_getitem(PyObject *self, PyObject *args)
{
PyObject *type, *item;
if (!PyArg_UnpackTuple(args, "__class_getitem__", 2, 2, &type, &item)) {
return NULL;
}
return generic_alias_new(item);
}

static PyMethodDef generic_methods[] = {
{"__class_getitem__", generic_class_getitem, METH_VARARGS|METH_STATIC, NULL},
{NULL} /* sentinel */
};

PyTypeObject Generic_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
"Generic",
sizeof(PyGenericObject),
0,
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
.tp_methods = generic_methods,
};


static struct PyModuleDef _testcapimodule = {
PyModuleDef_HEAD_INIT,
"_testcapi",
Expand Down Expand Up @@ -5094,6 +5169,16 @@ PyInit__testcapi(void)
Py_INCREF(&awaitType);
PyModule_AddObject(m, "awaitType", (PyObject *)&awaitType);

if (PyType_Ready(&GenericAlias_Type) < 0)
return NULL;
Py_INCREF(&GenericAlias_Type);
PyModule_AddObject(m, "GenericAlias", (PyObject *)&GenericAlias_Type);

if (PyType_Ready(&Generic_Type) < 0)
return NULL;
Py_INCREF(&Generic_Type);
PyModule_AddObject(m, "Generic", (PyObject *)&Generic_Type);

PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception;
if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) {
return NULL;
Expand Down

0 comments on commit 45700fb

Please sign in to comment.