From ac28147e78c45a6217d348ce90ca5281d91f676f Mon Sep 17 00:00:00 2001 From: Ivan Levkivskyi Date: Sun, 17 Feb 2019 23:13:46 +0000 Subject: [PATCH] bpo-35992: Use PySequence_GetItem only if sq_item is not NULL (GH-11857) Not using `__class_getitem__()` fallback if there is a non-subcriptable metaclass was caused by a certain asymmetry between how `PySequenceMethods` and `PyMappingMethods` are used in `PyObject_GetItem`. This PR removes this asymmetry. No tests failed, so I assume it was not intentional. --- Lib/test/test_genericclass.py | 9 ++++++++- .../2019-02-14-12-01-44.bpo-35992.nG9e2L.rst | 2 ++ Objects/abstract.c | 7 +++++-- 3 files changed, 15 insertions(+), 3 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2019-02-14-12-01-44.bpo-35992.nG9e2L.rst diff --git a/Lib/test/test_genericclass.py b/Lib/test/test_genericclass.py index 37e755b1167..37a87bc6815 100644 --- a/Lib/test/test_genericclass.py +++ b/Lib/test/test_genericclass.py @@ -248,7 +248,14 @@ class TestClassGetitem(unittest.TestCase): return f'{cls.__name__}[{item.__name__}]' self.assertEqual(Meta[int], 'Meta[int]') - def test_class_getitem_metaclass_2(self): + def test_class_getitem_with_metaclass(self): + class Meta(type): pass + class C(metaclass=Meta): + def __class_getitem__(cls, item): + return f'{cls.__name__}[{item.__name__}]' + self.assertEqual(C[int], 'C[int]') + + def test_class_getitem_metaclass_first(self): class Meta(type): def __getitem__(cls, item): return 'from metaclass' diff --git a/Misc/NEWS.d/next/Core and Builtins/2019-02-14-12-01-44.bpo-35992.nG9e2L.rst b/Misc/NEWS.d/next/Core and Builtins/2019-02-14-12-01-44.bpo-35992.nG9e2L.rst new file mode 100644 index 00000000000..3d8dcd48cd0 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2019-02-14-12-01-44.bpo-35992.nG9e2L.rst @@ -0,0 +1,2 @@ +Fix ``__class_getitem__()`` not being called on a class with a custom +non-subscriptable metaclass. diff --git a/Objects/abstract.c b/Objects/abstract.c index 567da2d6b50..0565ba34f5b 100644 --- a/Objects/abstract.c +++ b/Objects/abstract.c @@ -143,6 +143,7 @@ PyObject * PyObject_GetItem(PyObject *o, PyObject *key) { PyMappingMethods *m; + PySequenceMethods *ms; if (o == NULL || key == NULL) { return null_error(); @@ -155,7 +156,8 @@ PyObject_GetItem(PyObject *o, PyObject *key) return item; } - if (o->ob_type->tp_as_sequence) { + ms = o->ob_type->tp_as_sequence; + if (ms && ms->sq_item) { if (PyIndex_Check(key)) { Py_ssize_t key_value; key_value = PyNumber_AsSsize_t(key, PyExc_IndexError); @@ -163,9 +165,10 @@ PyObject_GetItem(PyObject *o, PyObject *key) return NULL; return PySequence_GetItem(o, key_value); } - else if (o->ob_type->tp_as_sequence->sq_item) + else { return type_error("sequence index must " "be integer, not '%.200s'", key); + } } if (PyType_Check(o)) {