diff options
-rw-r--r-- | Lib/test/test_genericclass.py | 18 | ||||
-rw-r--r-- | Modules/_testcapimodule.c | 85 |
2 files changed, 103 insertions, 0 deletions
diff --git a/Lib/test/test_genericclass.py b/Lib/test/test_genericclass.py index 214527b..2057fc0 100644 --- a/Lib/test/test_genericclass.py +++ b/Lib/test/test_genericclass.py @@ -1,4 +1,5 @@ import unittest +from test import support class TestMROEntry(unittest.TestCase): @@ -248,5 +249,22 @@ class TestClassGetitem(unittest.TestCase): 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() diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 4bb3e82..47b5064 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -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", @@ -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; |