Skip to content

Commit

Permalink
pythongh-104600: Make type.__type_params__ writable
Browse files Browse the repository at this point in the history
  • Loading branch information
JelleZijlstra committed May 18, 2023
1 parent 94c8eda commit 41b9279
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 7 deletions.
13 changes: 13 additions & 0 deletions Lib/test/test_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import sys
import traceback
import types
import typing
import unittest
import warnings
from contextlib import ExitStack
Expand Down Expand Up @@ -2485,6 +2486,18 @@ def test_type_qualname(self):
A.__qualname__ = b'B'
self.assertEqual(A.__qualname__, 'D.E')

def test_type_typeparams(self):
class A[T]:
pass
T, = A.__type_params__
self.assertIsInstance(T, typing.TypeVar)
A.__type_params__ = "whatever"
self.assertEqual(A.__type_params__, "whatever")
del A.__type_params__
self.assertEqual(A.__type_params__, ())
with self.assertRaises(AttributeError):
del A.__type_params__

def test_type_doc(self):
for doc in 'x', '\xc4', '\U0001f40d', 'x\x00y', b'x', 42, None:
A = type('A', (), {'__doc__': doc})
Expand Down
44 changes: 37 additions & 7 deletions Objects/typeobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -1460,6 +1460,36 @@ type_get_annotations(PyTypeObject *type, void *context)
return annotations;
}

static int
type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
{
if (_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE)) {
PyErr_Format(PyExc_TypeError,
"cannot set '__annotations__' attribute of immutable type '%s'",
type->tp_name);
return -1;
}

int result;
PyObject *dict = lookup_tp_dict(type);
if (value != NULL) {
/* set */
result = PyDict_SetItem(dict, &_Py_ID(__annotations__), value);
} else {
/* delete */
if (!PyDict_Contains(dict, &_Py_ID(__annotations__))) {
PyErr_Format(PyExc_AttributeError, "__annotations__");
return -1;
}
result = PyDict_DelItem(dict, &_Py_ID(__annotations__));
}

if (result == 0) {
PyType_Modified(type);
}
return result;
}

static PyObject *
type_get_type_params(PyTypeObject *type, void *context)
{
Expand All @@ -1473,11 +1503,11 @@ type_get_type_params(PyTypeObject *type, void *context)
}

static int
type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
type_set_type_params(PyTypeObject *type, PyObject *value, void *context)
{
if (_PyType_HasFeature(type, Py_TPFLAGS_IMMUTABLETYPE)) {
PyErr_Format(PyExc_TypeError,
"cannot set '__annotations__' attribute of immutable type '%s'",
"cannot set '__type_params__' attribute of immutable type '%s'",
type->tp_name);
return -1;
}
Expand All @@ -1486,14 +1516,14 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
PyObject *dict = lookup_tp_dict(type);
if (value != NULL) {
/* set */
result = PyDict_SetItem(dict, &_Py_ID(__annotations__), value);
result = PyDict_SetItem(dict, &_Py_ID(__type_params__), value);
} else {
/* delete */
if (!PyDict_Contains(dict, &_Py_ID(__annotations__))) {
PyErr_Format(PyExc_AttributeError, "__annotations__");
if (!PyDict_Contains(dict, &_Py_ID(__type_params__))) {
PyErr_Format(PyExc_AttributeError, "__type_params__");
return -1;
}
result = PyDict_DelItem(dict, &_Py_ID(__annotations__));
result = PyDict_DelItem(dict, &_Py_ID(__type_params__));
}

if (result == 0) {
Expand Down Expand Up @@ -1548,7 +1578,7 @@ static PyGetSetDef type_getsets[] = {
{"__doc__", (getter)type_get_doc, (setter)type_set_doc, NULL},
{"__text_signature__", (getter)type_get_text_signature, NULL, NULL},
{"__annotations__", (getter)type_get_annotations, (setter)type_set_annotations, NULL},
{"__type_params__", (getter)type_get_type_params, NULL, NULL},
{"__type_params__", (getter)type_get_type_params, (setter)type_set_type_params, NULL},
{0}
};

Expand Down

0 comments on commit 41b9279

Please sign in to comment.