]> granicus.if.org Git - python/commitdiff
Add tests for using PEP560 with classes implemented in C. (#4883)
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 16 Dec 2017 09:25:56 +0000 (11:25 +0200)
committerIvan Levkivskyi <levkivskyi@gmail.com>
Sat, 16 Dec 2017 09:25:56 +0000 (10:25 +0100)
Based on tests from #4878

Lib/test/test_genericclass.py
Modules/_testcapimodule.c

index 214527b01fa871762cc4b01a1e2c3ca76b73a619..2057fc000c0d701aa1058d7faca2649678e6a967 100644 (file)
@@ -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()
index 4bb3e82d1dcfee7fb100e5593215bd9f878581d3..47b50640f5d8be2c0ddbed5d6a2549fc71b1ac5b 100644 (file)
@@ -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;