mirror of https://github.com/python/cpython
bpo-32226: Make __class_getitem__ an automatic class method. (#5098)
This commit is contained in:
parent
87be28f4a1
commit
ce5b0e9db1
|
@ -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)
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
``__class_getitem__`` is now an automatic class method.
|
|
@ -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 */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue