[Python-checkins] gh-104600: Make type.__type_params__ writable (#104634)

JelleZijlstra webhook-mailer at python.org
Fri May 19 12:04:57 EDT 2023


https://github.com/python/cpython/commit/8f1f3b9abdaa3e9d19aad22d6c310eb1f05ae5c2
commit: 8f1f3b9abdaa3e9d19aad22d6c310eb1f05ae5c2
branch: main
author: Jelle Zijlstra <jelle.zijlstra at gmail.com>
committer: JelleZijlstra <jelle.zijlstra at gmail.com>
date: 2023-05-19T09:04:47-07:00
summary:

gh-104600: Make type.__type_params__ writable (#104634)

Co-authored-by: Alex Waygood <Alex.Waygood at Gmail.com>

files:
M Lib/test/test_builtin.py
M Lib/test/test_type_params.py
M Lib/test/test_typing.py
M Objects/typeobject.c

diff --git a/Lib/test/test_builtin.py b/Lib/test/test_builtin.py
index 821710a7fa32..1257b529038a 100644
--- a/Lib/test/test_builtin.py
+++ b/Lib/test/test_builtin.py
@@ -18,6 +18,7 @@
 import sys
 import traceback
 import types
+import typing
 import unittest
 import warnings
 from contextlib import ExitStack
@@ -2485,6 +2486,17 @@ 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")
+        with self.assertRaises(TypeError):
+            del A.__type_params__
+        self.assertEqual(A.__type_params__, "whatever")
+
     def test_type_doc(self):
         for doc in 'x', '\xc4', '\U0001f40d', 'x\x00y', b'x', 42, None:
             A = type('A', (), {'__doc__': doc})
diff --git a/Lib/test/test_type_params.py b/Lib/test/test_type_params.py
index d4f5de573f51..7b7b6122c028 100644
--- a/Lib/test/test_type_params.py
+++ b/Lib/test/test_type_params.py
@@ -816,10 +816,11 @@ def test_typeparams_dunder_class_03(self):
             class ClassA[A]():
                 pass
             ClassA.__type_params__ = ()
+            params = ClassA.__type_params__
         """
 
-        with self.assertRaisesRegex(AttributeError, "attribute '__type_params__' of 'type' objects is not writable"):
-            run_code(code)
+        ns = run_code(code)
+        self.assertEqual(ns["params"], ())
 
     def test_typeparams_dunder_function_01(self):
         def outer[A, B]():
diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py
index 450c85967dd7..6459fa3eb96a 100644
--- a/Lib/test/test_typing.py
+++ b/Lib/test/test_typing.py
@@ -6810,6 +6810,19 @@ class Y(Generic[T], NamedTuple):
                 with self.assertRaises(TypeError):
                     G[int, str]
 
+    def test_generic_pep695(self):
+        class X[T](NamedTuple):
+            x: T
+        T, = X.__type_params__
+        self.assertIsInstance(T, TypeVar)
+        self.assertEqual(T.__name__, 'T')
+        self.assertEqual(X.__bases__, (tuple, Generic))
+        self.assertEqual(X.__orig_bases__, (NamedTuple, Generic[T]))
+        self.assertEqual(X.__mro__, (X, tuple, Generic, object))
+        self.assertEqual(X.__parameters__, (T,))
+        self.assertEqual(X[str].__args__, (str,))
+        self.assertEqual(X[str].__parameters__, ())
+
     def test_non_generic_subscript(self):
         # For backward compatibility, subscription works
         # on arbitrary NamedTuple types.
@@ -7220,6 +7233,20 @@ class FooBarGeneric(BarGeneric[int]):
             {'a': typing.Optional[T], 'b': int, 'c': str}
         )
 
+    def test_pep695_generic_typeddict(self):
+        class A[T](TypedDict):
+            a: T
+
+        T, = A.__type_params__
+        self.assertIsInstance(T, TypeVar)
+        self.assertEqual(T.__name__, 'T')
+        self.assertEqual(A.__bases__, (Generic, dict))
+        self.assertEqual(A.__orig_bases__, (TypedDict, Generic[T]))
+        self.assertEqual(A.__mro__, (A, Generic, dict, object))
+        self.assertEqual(A.__parameters__, (T,))
+        self.assertEqual(A[str].__parameters__, ())
+        self.assertEqual(A[str].__args__, (str,))
+
     def test_generic_inheritance(self):
         class A(TypedDict, Generic[T]):
             a: T
diff --git a/Objects/typeobject.c b/Objects/typeobject.c
index 624dc63ce82c..2fbcafe91aad 100644
--- a/Objects/typeobject.c
+++ b/Objects/typeobject.c
@@ -1460,18 +1460,6 @@ type_get_annotations(PyTypeObject *type, void *context)
     return annotations;
 }
 
-static PyObject *
-type_get_type_params(PyTypeObject *type, void *context)
-{
-    PyObject *params = PyDict_GetItem(lookup_tp_dict(type), &_Py_ID(__type_params__));
-
-    if (params) {
-        return Py_NewRef(params);
-    }
-
-    return PyTuple_New(0);
-}
-
 static int
 type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
 {
@@ -1502,6 +1490,34 @@ type_set_annotations(PyTypeObject *type, PyObject *value, void *context)
     return result;
 }
 
+static PyObject *
+type_get_type_params(PyTypeObject *type, void *context)
+{
+    PyObject *params = PyDict_GetItem(lookup_tp_dict(type), &_Py_ID(__type_params__));
+
+    if (params) {
+        return Py_NewRef(params);
+    }
+
+    return PyTuple_New(0);
+}
+
+static int
+type_set_type_params(PyTypeObject *type, PyObject *value, void *context)
+{
+    if (!check_set_special_type_attr(type, value, "__type_params__")) {
+        return -1;
+    }
+
+    PyObject *dict = lookup_tp_dict(type);
+    int result = PyDict_SetItem(dict, &_Py_ID(__type_params__), value);
+
+    if (result == 0) {
+        PyType_Modified(type);
+    }
+    return result;
+}
+
 
 /*[clinic input]
 type.__instancecheck__ -> bool
@@ -1548,7 +1564,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}
 };
 



More information about the Python-checkins mailing list