Skip to content

Commit a373a4f

Browse files
committed
Ported @Gobot1234's function-subscript branch from 3.13a0 to main (3.16)
1 parent 04242c0 commit a373a4f

5 files changed

Lines changed: 75 additions & 6 deletions

File tree

Lib/test/test_genericalias.py

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,10 @@
4949
ShareableList = None
5050
from os import DirEntry
5151
from re import Pattern, Match
52-
from types import GenericAlias, MappingProxyType, AsyncGeneratorType, CoroutineType, GeneratorType
52+
from types import (
53+
AsyncGeneratorType, CoroutineType, GeneratorType, GenericAlias,
54+
MappingProxyType,
55+
)
5356
from tempfile import TemporaryDirectory, SpooledTemporaryFile
5457
from urllib.parse import SplitResult, ParseResult
5558
from unittest.case import _AssertRaisesContext
@@ -98,11 +101,22 @@
98101
]
99102

100103

104+
def generic_function[T]():
105+
pass
106+
107+
108+
class GenericMethod:
109+
def method[T](self):
110+
pass
111+
112+
101113
class BaseTest(unittest.TestCase):
102114
"""Test basics."""
103115
generic_types = [type, tuple, list, dict, frozendict,
104116
set, frozenset, enumerate, memoryview,
105117
slice,
118+
generic_function, GenericMethod().method, max,
119+
dict.fromkeys,
106120
defaultdict, deque,
107121
SequenceMatcher,
108122
dircmp,
@@ -214,6 +228,26 @@ def test_no_chaining(self):
214228
with self.assertRaises(TypeError):
215229
t[int]
216230

231+
class Dummy:
232+
pass
233+
234+
def test_callable_alias_does_not_set_orig_class(self):
235+
dummy_type = self.Dummy
236+
237+
def function[T]():
238+
result = dummy_type()
239+
result.__orig_class__ = str
240+
return result
241+
242+
class Class:
243+
def method[T](self):
244+
result = dummy_type()
245+
result.__orig_class__ = str
246+
return result
247+
248+
self.assertIs(function[int]().__orig_class__, str)
249+
self.assertIs(Class().method[int]().__orig_class__, str)
250+
217251
def test_generic_subclass(self):
218252
class MyList(list):
219253
pass

Objects/classobject.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,13 +310,24 @@ method_descr_get(PyObject *meth, PyObject *obj, PyObject *cls)
310310
return meth;
311311
}
312312

313+
static PyObject *
314+
method_getitem(PyObject *self, PyObject *item)
315+
{
316+
return Py_GenericAlias(self, item);
317+
}
318+
319+
static PyMappingMethods method_as_mapping = {
320+
.mp_subscript = method_getitem,
321+
};
322+
313323
PyTypeObject PyMethod_Type = {
314324
PyVarObject_HEAD_INIT(&PyType_Type, 0)
315325
.tp_name = "method",
316326
.tp_basicsize = sizeof(PyMethodObject),
317327
.tp_dealloc = method_dealloc,
318328
.tp_vectorcall_offset = offsetof(PyMethodObject, vectorcall),
319329
.tp_repr = method_repr,
330+
.tp_as_mapping = &method_as_mapping,
320331
.tp_hash = method_hash,
321332
.tp_call = PyVectorcall_Call,
322333
.tp_getattro = method_getattro,

Objects/funcobject.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,6 +1241,16 @@ func_repr(PyObject *self)
12411241
op->func_qualname, op);
12421242
}
12431243

1244+
static PyObject *
1245+
func_getitem(PyObject *self, PyObject *item)
1246+
{
1247+
return Py_GenericAlias(self, item);
1248+
}
1249+
1250+
static PyMappingMethods func_as_mapping = {
1251+
.mp_subscript = func_getitem,
1252+
};
1253+
12441254
static int
12451255
func_traverse(PyObject *self, visitproc visit, void *arg)
12461256
{
@@ -1285,7 +1295,7 @@ PyTypeObject PyFunction_Type = {
12851295
func_repr, /* tp_repr */
12861296
0, /* tp_as_number */
12871297
0, /* tp_as_sequence */
1288-
0, /* tp_as_mapping */
1298+
&func_as_mapping, /* tp_as_mapping */
12891299
0, /* tp_hash */
12901300
PyVectorcall_Call, /* tp_call */
12911301
0, /* tp_str */

Objects/genericaliasobject.c

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -643,8 +643,12 @@ ga_hash(PyObject *self)
643643
}
644644

645645
static inline PyObject *
646-
set_orig_class(PyObject *obj, PyObject *self)
646+
set_orig_class(PyObject *obj, PyObject *self, PyObject *origin)
647647
{
648+
if (PyFunction_Check(origin) || PyMethod_Check(origin) ||
649+
PyCFunction_Check(origin)) {
650+
return obj;
651+
}
648652
if (obj != NULL) {
649653
if (PyObject_SetAttr(obj, &_Py_ID(__orig_class__), self) < 0) {
650654
if (!PyErr_ExceptionMatches(PyExc_AttributeError) &&
@@ -664,7 +668,7 @@ ga_call(PyObject *self, PyObject *args, PyObject *kwds)
664668
{
665669
gaobject *alias = (gaobject *)self;
666670
PyObject *obj = PyObject_Call(alias->origin, args, kwds);
667-
return set_orig_class(obj, self);
671+
return set_orig_class(obj, self, alias->origin);
668672
}
669673

670674
static PyObject *
@@ -673,7 +677,7 @@ ga_vectorcall(PyObject *self, PyObject *const *args,
673677
{
674678
gaobject *alias = (gaobject *) self;
675679
PyObject *obj = PyObject_Vectorcall(alias->origin, args, nargsf, kwnames);
676-
return set_orig_class(obj, self);
680+
return set_orig_class(obj, self, alias->origin);
677681
}
678682

679683
static const char* const attr_exceptions[] = {

Objects/methodobject.c

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,16 @@ meth_repr(PyObject *self)
314314
m->m_self);
315315
}
316316

317+
static PyObject *
318+
meth_getitem(PyObject *self, PyObject *item)
319+
{
320+
return Py_GenericAlias(self, item);
321+
}
322+
323+
static PyMappingMethods meth_as_mapping = {
324+
.mp_subscript = meth_getitem,
325+
};
326+
317327
static PyObject *
318328
meth_richcompare(PyObject *self, PyObject *other, int op)
319329
{
@@ -366,7 +376,7 @@ PyTypeObject PyCFunction_Type = {
366376
meth_repr, /* tp_repr */
367377
0, /* tp_as_number */
368378
0, /* tp_as_sequence */
369-
0, /* tp_as_mapping */
379+
&meth_as_mapping, /* tp_as_mapping */
370380
meth_hash, /* tp_hash */
371381
cfunction_call, /* tp_call */
372382
0, /* tp_str */

0 commit comments

Comments
 (0)