Skip to content

Commit 45700fb

Browse files
serhiy-storchakailevkivskyi
authored andcommitted
Add tests for using PEP560 with classes implemented in C. (#4883)
Based on tests from #4878
1 parent 9c19b02 commit 45700fb

File tree

2 files changed

+103
-0
lines changed

2 files changed

+103
-0
lines changed

Lib/test/test_genericclass.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import unittest
2+
from test import support
23

34

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

250251

252+
@support.cpython_only
253+
class CAPITest(unittest.TestCase):
254+
255+
def test_c_class(self):
256+
from _testcapi import Generic, GenericAlias
257+
self.assertIsInstance(Generic.__class_getitem__(Generic, int), GenericAlias)
258+
259+
IntGeneric = Generic[int]
260+
self.assertIs(type(IntGeneric), GenericAlias)
261+
self.assertEqual(IntGeneric.__mro_entries__(()), (int,))
262+
class C(IntGeneric):
263+
pass
264+
self.assertEqual(C.__bases__, (int,))
265+
self.assertEqual(C.__orig_bases__, (IntGeneric,))
266+
self.assertEqual(C.__mro__, (C, int, object))
267+
268+
251269
if __name__ == "__main__":
252270
unittest.main()

Modules/_testcapimodule.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5053,6 +5053,81 @@ recurse_infinitely_error_init(PyObject *self, PyObject *args, PyObject *kwds)
50535053
}
50545054

50555055

5056+
/* Test PEP 560 */
5057+
5058+
typedef struct {
5059+
PyObject_HEAD
5060+
PyObject *item;
5061+
} PyGenericAliasObject;
5062+
5063+
static void
5064+
generic_alias_dealloc(PyGenericAliasObject *self)
5065+
{
5066+
Py_CLEAR(self->item);
5067+
}
5068+
5069+
static PyObject *
5070+
generic_alias_mro_entries(PyGenericAliasObject *self, PyObject *bases)
5071+
{
5072+
return PyTuple_Pack(1, self->item);
5073+
}
5074+
5075+
static PyMethodDef generic_alias_methods[] = {
5076+
{"__mro_entries__", (PyCFunction) generic_alias_mro_entries, METH_O, NULL},
5077+
{NULL} /* sentinel */
5078+
};
5079+
5080+
PyTypeObject GenericAlias_Type = {
5081+
PyVarObject_HEAD_INIT(NULL, 0)
5082+
"GenericAlias",
5083+
sizeof(PyGenericAliasObject),
5084+
0,
5085+
.tp_dealloc = (destructor)generic_alias_dealloc,
5086+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
5087+
.tp_methods = generic_alias_methods,
5088+
};
5089+
5090+
static PyObject *
5091+
generic_alias_new(PyObject *item)
5092+
{
5093+
PyGenericAliasObject *o = PyObject_New(PyGenericAliasObject, &GenericAlias_Type);
5094+
if (o == NULL) {
5095+
return NULL;
5096+
}
5097+
Py_INCREF(item);
5098+
o->item = item;
5099+
return (PyObject*) o;
5100+
}
5101+
5102+
typedef struct {
5103+
PyObject_HEAD
5104+
} PyGenericObject;
5105+
5106+
static PyObject *
5107+
generic_class_getitem(PyObject *self, PyObject *args)
5108+
{
5109+
PyObject *type, *item;
5110+
if (!PyArg_UnpackTuple(args, "__class_getitem__", 2, 2, &type, &item)) {
5111+
return NULL;
5112+
}
5113+
return generic_alias_new(item);
5114+
}
5115+
5116+
static PyMethodDef generic_methods[] = {
5117+
{"__class_getitem__", generic_class_getitem, METH_VARARGS|METH_STATIC, NULL},
5118+
{NULL} /* sentinel */
5119+
};
5120+
5121+
PyTypeObject Generic_Type = {
5122+
PyVarObject_HEAD_INIT(NULL, 0)
5123+
"Generic",
5124+
sizeof(PyGenericObject),
5125+
0,
5126+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
5127+
.tp_methods = generic_methods,
5128+
};
5129+
5130+
50565131
static struct PyModuleDef _testcapimodule = {
50575132
PyModuleDef_HEAD_INIT,
50585133
"_testcapi",
@@ -5094,6 +5169,16 @@ PyInit__testcapi(void)
50945169
Py_INCREF(&awaitType);
50955170
PyModule_AddObject(m, "awaitType", (PyObject *)&awaitType);
50965171

5172+
if (PyType_Ready(&GenericAlias_Type) < 0)
5173+
return NULL;
5174+
Py_INCREF(&GenericAlias_Type);
5175+
PyModule_AddObject(m, "GenericAlias", (PyObject *)&GenericAlias_Type);
5176+
5177+
if (PyType_Ready(&Generic_Type) < 0)
5178+
return NULL;
5179+
Py_INCREF(&Generic_Type);
5180+
PyModule_AddObject(m, "Generic", (PyObject *)&Generic_Type);
5181+
50975182
PyRecursingInfinitelyError_Type.tp_base = (PyTypeObject *)PyExc_Exception;
50985183
if (PyType_Ready(&PyRecursingInfinitelyError_Type) < 0) {
50995184
return NULL;

0 commit comments

Comments
 (0)