diff --git a/Lib/test/test_genericclass.py b/Lib/test/test_genericclass.py index 2057fc000c0..37e755b1167 100644 --- a/Lib/test/test_genericclass.py +++ b/Lib/test/test_genericclass.py @@ -183,12 +183,21 @@ def __class_getitem__(cls, item): self.assertEqual(D[int], 'D[int]') self.assertEqual(D[D], 'D[D]') + def test_class_getitem_classmethod(self): + class C: + @classmethod + def __class_getitem__(cls, item): + return f'{cls.__name__}[{item.__name__}]' + class D(C): ... + self.assertEqual(D[int], 'D[int]') + self.assertEqual(D[D], 'D[D]') + def test_class_getitem_patched(self): class C: def __init_subclass__(cls): def __class_getitem__(cls, item): return f'{cls.__name__}[{item.__name__}]' - cls.__class_getitem__ = __class_getitem__ + cls.__class_getitem__ = classmethod(__class_getitem__) class D(C): ... self.assertEqual(D[int], 'D[int]') self.assertEqual(D[D], 'D[D]') @@ -254,7 +263,7 @@ class CAPITest(unittest.TestCase): def test_c_class(self): from _testcapi import Generic, GenericAlias - self.assertIsInstance(Generic.__class_getitem__(Generic, int), GenericAlias) + self.assertIsInstance(Generic.__class_getitem__(int), GenericAlias) IntGeneric = Generic[int] self.assertIs(type(IntGeneric), GenericAlias) diff --git a/Misc/NEWS.d/next/Core and Builtins/2018-01-04-15-06-15.bpo-32226.7cAvRG.rst b/Misc/NEWS.d/next/Core and Builtins/2018-01-04-15-06-15.bpo-32226.7cAvRG.rst new file mode 100644 index 00000000000..842775f3b93 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2018-01-04-15-06-15.bpo-32226.7cAvRG.rst @@ -0,0 +1 @@ +``__class_getitem__`` is now an automatic class method. diff --git a/Modules/_testcapimodule.c b/Modules/_testcapimodule.c index 47b50640f5d..7b2e2a3e5a4 100644 --- a/Modules/_testcapimodule.c +++ b/Modules/_testcapimodule.c @@ -5104,17 +5104,13 @@ typedef struct { } PyGenericObject; static PyObject * -generic_class_getitem(PyObject *self, PyObject *args) +generic_class_getitem(PyObject *type, PyObject *item) { - 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}, + {"__class_getitem__", generic_class_getitem, METH_O|METH_CLASS, NULL}, {NULL} /* sentinel */ }; diff --git a/Objects/abstract.c b/Objects/abstract.c index 0105c5d1696..a68253bdb43 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -169,11 +169,11 @@ PyObject_GetItem(PyObject *o, PyObject *key) } if (PyType_Check(o)) { - PyObject *meth, *result, *stack[2] = {o, key}; + PyObject *meth, *result, *stack[1] = {key}; _Py_IDENTIFIER(__class_getitem__); meth = _PyObject_GetAttrId(o, &PyId___class_getitem__); if (meth) { - result = _PyObject_FastCall(meth, stack, 2); + result = _PyObject_FastCall(meth, stack, 1); Py_DECREF(meth); return result; } diff --git a/Objects/typeobject.c b/Objects/typeobject.c index e570c4145df..0c8f0adfb78 100644 --- a/Objects/typeobject.c +++ b/Objects/typeobject.c @@ -55,6 +55,7 @@ static size_t method_cache_collisions = 0; /* alphabetical order */ _Py_IDENTIFIER(__abstractmethods__); _Py_IDENTIFIER(__class__); +_Py_IDENTIFIER(__class_getitem__); _Py_IDENTIFIER(__delitem__); _Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(__doc__); @@ -2694,8 +2695,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) Py_DECREF(tmp); } - /* Special-case __init_subclass__: if it's a plain function, - make it a classmethod */ + /* Special-case __init_subclass__ and __class_getitem__: + if they are plain functions, make them classmethods */ tmp = _PyDict_GetItemId(dict, &PyId___init_subclass__); if (tmp != NULL && PyFunction_Check(tmp)) { tmp = PyClassMethod_New(tmp); @@ -2708,6 +2709,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds) Py_DECREF(tmp); } + tmp = _PyDict_GetItemId(dict, &PyId___class_getitem__); + if (tmp != NULL && PyFunction_Check(tmp)) { + tmp = PyClassMethod_New(tmp); + if (tmp == NULL) + goto error; + if (_PyDict_SetItemId(dict, &PyId___class_getitem__, tmp) < 0) { + Py_DECREF(tmp); + goto error; + } + Py_DECREF(tmp); + } + /* Add descriptors for custom slots from __slots__, or for __dict__ */ mp = PyHeapType_GET_MEMBERS(et); slotoffset = base->tp_basicsize;