Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion Lib/test/test_genericalias.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Comment on lines -52 to +55

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems redundant (it's just reformatting an import without adding or removing anything).

from tempfile import TemporaryDirectory, SpooledTemporaryFile
from urllib.parse import SplitResult, ParseResult
from unittest.case import _AssertRaisesContext
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down
11 changes: 11 additions & 0 deletions Objects/classobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -310,13 +310,24 @@ 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",
.tp_basicsize = sizeof(PyMethodObject),
.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,
Expand Down
12 changes: 11 additions & 1 deletion Objects/funcobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 */
Expand Down
10 changes: 7 additions & 3 deletions Objects/genericaliasobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) &&
Expand All @@ -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 *
Expand All @@ -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[] = {
Expand Down
12 changes: 11 additions & 1 deletion Objects/methodobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 */
Expand Down
Loading