bpo-32226: Make __class_getitem__ an automatic class method. (#5098)

This commit is contained in:
Serhiy Storchaka 2018-01-05 00:21:41 +02:00 committed by GitHub
parent 87be28f4a1
commit ce5b0e9db1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 31 additions and 12 deletions

View File

@ -183,12 +183,21 @@ class TestClassGetitem(unittest.TestCase):
self.assertEqual(D[int], 'D[int]') self.assertEqual(D[int], 'D[int]')
self.assertEqual(D[D], 'D[D]') 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): def test_class_getitem_patched(self):
class C: class C:
def __init_subclass__(cls): def __init_subclass__(cls):
def __class_getitem__(cls, item): def __class_getitem__(cls, item):
return f'{cls.__name__}[{item.__name__}]' return f'{cls.__name__}[{item.__name__}]'
cls.__class_getitem__ = __class_getitem__ cls.__class_getitem__ = classmethod(__class_getitem__)
class D(C): ... class D(C): ...
self.assertEqual(D[int], 'D[int]') self.assertEqual(D[int], 'D[int]')
self.assertEqual(D[D], 'D[D]') self.assertEqual(D[D], 'D[D]')
@ -254,7 +263,7 @@ class CAPITest(unittest.TestCase):
def test_c_class(self): def test_c_class(self):
from _testcapi import Generic, GenericAlias from _testcapi import Generic, GenericAlias
self.assertIsInstance(Generic.__class_getitem__(Generic, int), GenericAlias) self.assertIsInstance(Generic.__class_getitem__(int), GenericAlias)
IntGeneric = Generic[int] IntGeneric = Generic[int]
self.assertIs(type(IntGeneric), GenericAlias) self.assertIs(type(IntGeneric), GenericAlias)

View File

@ -0,0 +1 @@
``__class_getitem__`` is now an automatic class method.

View File

@ -5104,17 +5104,13 @@ typedef struct {
} PyGenericObject; } PyGenericObject;
static PyObject * 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); return generic_alias_new(item);
} }
static PyMethodDef generic_methods[] = { 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 */ {NULL} /* sentinel */
}; };

View File

@ -169,11 +169,11 @@ PyObject_GetItem(PyObject *o, PyObject *key)
} }
if (PyType_Check(o)) { if (PyType_Check(o)) {
PyObject *meth, *result, *stack[2] = {o, key}; PyObject *meth, *result, *stack[1] = {key};
_Py_IDENTIFIER(__class_getitem__); _Py_IDENTIFIER(__class_getitem__);
meth = _PyObject_GetAttrId(o, &PyId___class_getitem__); meth = _PyObject_GetAttrId(o, &PyId___class_getitem__);
if (meth) { if (meth) {
result = _PyObject_FastCall(meth, stack, 2); result = _PyObject_FastCall(meth, stack, 1);
Py_DECREF(meth); Py_DECREF(meth);
return result; return result;
} }

View File

@ -55,6 +55,7 @@ static size_t method_cache_collisions = 0;
/* alphabetical order */ /* alphabetical order */
_Py_IDENTIFIER(__abstractmethods__); _Py_IDENTIFIER(__abstractmethods__);
_Py_IDENTIFIER(__class__); _Py_IDENTIFIER(__class__);
_Py_IDENTIFIER(__class_getitem__);
_Py_IDENTIFIER(__delitem__); _Py_IDENTIFIER(__delitem__);
_Py_IDENTIFIER(__dict__); _Py_IDENTIFIER(__dict__);
_Py_IDENTIFIER(__doc__); _Py_IDENTIFIER(__doc__);
@ -2694,8 +2695,8 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
Py_DECREF(tmp); Py_DECREF(tmp);
} }
/* Special-case __init_subclass__: if it's a plain function, /* Special-case __init_subclass__ and __class_getitem__:
make it a classmethod */ if they are plain functions, make them classmethods */
tmp = _PyDict_GetItemId(dict, &PyId___init_subclass__); tmp = _PyDict_GetItemId(dict, &PyId___init_subclass__);
if (tmp != NULL && PyFunction_Check(tmp)) { if (tmp != NULL && PyFunction_Check(tmp)) {
tmp = PyClassMethod_New(tmp); tmp = PyClassMethod_New(tmp);
@ -2708,6 +2709,18 @@ type_new(PyTypeObject *metatype, PyObject *args, PyObject *kwds)
Py_DECREF(tmp); 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__ */ /* Add descriptors for custom slots from __slots__, or for __dict__ */
mp = PyHeapType_GET_MEMBERS(et); mp = PyHeapType_GET_MEMBERS(et);
slotoffset = base->tp_basicsize; slotoffset = base->tp_basicsize;