diff --git a/Lib/test/test_genericalias.py b/Lib/test/test_genericalias.py index 7816775620bc01..a31a478a02d845 100644 --- a/Lib/test/test_genericalias.py +++ b/Lib/test/test_genericalias.py @@ -49,7 +49,10 @@ ShareableList = None from os import DirEntry from re import Pattern, Match -from types import GenericAlias, MappingProxyType, AsyncGeneratorType, CoroutineType, GeneratorType +from types import ( + AsyncGeneratorType, CoroutineType, GeneratorType, GenericAlias, + MappingProxyType, +) from tempfile import TemporaryDirectory, SpooledTemporaryFile from urllib.parse import SplitResult, ParseResult from unittest.case import _AssertRaisesContext @@ -98,11 +101,22 @@ ] +def generic_function[T](): + pass + + +class GenericMethod: + def method[T](self): + pass + + class BaseTest(unittest.TestCase): """Test basics.""" generic_types = [type, tuple, list, dict, frozendict, set, frozenset, enumerate, memoryview, slice, + generic_function, GenericMethod().method, max, + dict.fromkeys, defaultdict, deque, SequenceMatcher, dircmp, @@ -214,6 +228,26 @@ def test_no_chaining(self): with self.assertRaises(TypeError): t[int] + class Dummy: + pass + + def test_callable_alias_does_not_set_orig_class(self): + dummy_type = self.Dummy + + def function[T](): + result = dummy_type() + result.__orig_class__ = str + return result + + class Class: + def method[T](self): + result = dummy_type() + result.__orig_class__ = str + return result + + self.assertIs(function[int]().__orig_class__, str) + self.assertIs(Class().method[int]().__orig_class__, str) + def test_generic_subclass(self): class MyList(list): pass diff --git a/Objects/classobject.c b/Objects/classobject.c index 238f1c1dad7d86..22183209f3d1a6 100644 --- a/Objects/classobject.c +++ b/Objects/classobject.c @@ -310,6 +310,16 @@ method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls) return meth; } +static PyObject * +method_getitem(PyObject *self, PyObject *item) +{ + return Py_GenericAlias(self, item); +} + +static PyMappingMethods method_as_mapping = { + .mp_subscript = method_getitem, +}; + PyTypeObject PyMethod_Type = { PyVarObject_HEAD_INIT(&PyType_Type, 0) .tp_name = "method", @@ -317,6 +327,7 @@ PyTypeObject PyMethod_Type = { .tp_dealloc = method_dealloc, .tp_vectorcall_offset = offsetof(PyMethodObject, vectorcall), .tp_repr = method_repr, + .tp_as_mapping = &method_as_mapping, .tp_hash = method_hash, .tp_call = PyVectorcall_Call, .tp_getattro = method_getattro, diff --git a/Objects/funcobject.c b/Objects/funcobject.c index 0481adadf668f8..c87db0e93f6efd 100644 --- a/Objects/funcobject.c +++ b/Objects/funcobject.c @@ -1241,6 +1241,16 @@ func_repr(PyObject *self) op->func_qualname, op); } +static PyObject * +func_getitem(PyObject *self, PyObject *item) +{ + return Py_GenericAlias(self, item); +} + +static PyMappingMethods func_as_mapping = { + .mp_subscript = func_getitem, +}; + static int func_traverse(PyObject *self, visitproc visit, void *arg) { @@ -1285,7 +1295,7 @@ PyTypeObject PyFunction_Type = { func_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &func_as_mapping, /* tp_as_mapping */ 0, /* tp_hash */ PyVectorcall_Call, /* tp_call */ 0, /* tp_str */ diff --git a/Objects/genericaliasobject.c b/Objects/genericaliasobject.c index 8bb7cc8c74a592..5f2941daaa4ebb 100644 --- a/Objects/genericaliasobject.c +++ b/Objects/genericaliasobject.c @@ -643,8 +643,12 @@ ga_hash(PyObject *self) } static inline PyObject * -set_orig_class(PyObject *obj, PyObject *self) +set_orig_class(PyObject *obj, PyObject *self, PyObject *origin) { + if (PyFunction_Check(origin) || PyMethod_Check(origin) || + PyCFunction_Check(origin)) { + return obj; + } if (obj != NULL) { if (PyObject_SetAttr(obj, &_Py_ID(__orig_class__), self) < 0) { if (!PyErr_ExceptionMatches(PyExc_AttributeError) && @@ -664,7 +668,7 @@ ga_call(PyObject *self, PyObject *args, PyObject *kwds) { gaobject *alias = (gaobject *)self; PyObject *obj = PyObject_Call(alias->origin, args, kwds); - return set_orig_class(obj, self); + return set_orig_class(obj, self, alias->origin); } static PyObject * @@ -673,7 +677,7 @@ ga_vectorcall(PyObject *self, PyObject *const *args, { gaobject *alias = (gaobject *) self; PyObject *obj = PyObject_Vectorcall(alias->origin, args, nargsf, kwnames); - return set_orig_class(obj, self); + return set_orig_class(obj, self, alias->origin); } static const char* const attr_exceptions[] = { diff --git a/Objects/methodobject.c b/Objects/methodobject.c index e6e469ca270ac9..73d415dfd70b0c 100644 --- a/Objects/methodobject.c +++ b/Objects/methodobject.c @@ -314,6 +314,16 @@ meth_repr(PyObject *self) m->m_self); } +static PyObject * +meth_getitem(PyObject *self, PyObject *item) +{ + return Py_GenericAlias(self, item); +} + +static PyMappingMethods meth_as_mapping = { + .mp_subscript = meth_getitem, +}; + static PyObject * meth_richcompare(PyObject *self, PyObject *other, int op) { @@ -366,7 +376,7 @@ PyTypeObject PyCFunction_Type = { meth_repr, /* tp_repr */ 0, /* tp_as_number */ 0, /* tp_as_sequence */ - 0, /* tp_as_mapping */ + &meth_as_mapping, /* tp_as_mapping */ meth_hash, /* tp_hash */ cfunction_call, /* tp_call */ 0, /* tp_str */