]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-149171: Make TypeAliasType __module__ writable (#149172)
authorEric Mark Martin <eric@emm.dev>
Sat, 2 May 2026 17:28:08 +0000 (13:28 -0400)
committerGitHub <noreply@github.com>
Sat, 2 May 2026 17:28:08 +0000 (17:28 +0000)
closes #149171.

Doc/library/typing.rst
Lib/test/test_type_aliases.py
Misc/NEWS.d/next/Core_and_Builtins/2026-04-30-01-35-09.gh-issue-149171.meXWpl.rst [new file with mode: 0644]
Objects/typevarobject.c

index 1957cadcbb159269aed1757cf547c88c271bb1f0..17cf57dd00b4bda226986e0deacf99357e307599 100644 (file)
@@ -2361,6 +2361,12 @@ without the dedicated syntax, as documented below.
          >>> Alias.__module__
          '__main__'
 
+      This attribute is writable.
+
+      .. versionchanged:: 3.15
+
+         The attribute is now writable.
+
    .. attribute:: __type_params__
 
       The type parameters of the type alias, or an empty tuple if the alias is
index 9ceee565764c9a4bd7e1b5cd939ff6196cbee9c9..05bc6a80888a06770c3d3878b16c0f83c996d4b5 100644 (file)
@@ -372,6 +372,8 @@ class TypeAliasTypeTest(unittest.TestCase):
                          mod_generics_cache.__name__)
         self.assertEqual(mod_generics_cache.OldStyle.__module__,
                          mod_generics_cache.__name__)
+        Alias.__module__ = "ham.spam.eggs"
+        self.assertEqual(Alias.__module__, "ham.spam.eggs")
 
     def test_unpack(self):
         type Alias = tuple[int, int]
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2026-04-30-01-35-09.gh-issue-149171.meXWpl.rst b/Misc/NEWS.d/next/Core_and_Builtins/2026-04-30-01-35-09.gh-issue-149171.meXWpl.rst
new file mode 100644 (file)
index 0000000..f8320dd
--- /dev/null
@@ -0,0 +1,2 @@
+Allow assignment to the ``__module__`` attribute of
+:class:`typing.TypeAliasType` instances.
index cdc0ea42eac2631e45c0b2aa566c93c417cab8d0..8ad590cc6e60936ae8cabdab89042074892aeb77 100644 (file)
@@ -1983,8 +1983,12 @@ static PyObject *
 typealias_module(PyObject *self, void *Py_UNUSED(closure))
 {
     typealiasobject *ta = typealiasobject_CAST(self);
-    if (ta->module != NULL) {
-        return Py_NewRef(ta->module);
+    PyObject *module;
+    Py_BEGIN_CRITICAL_SECTION(self);
+    module = Py_XNewRef(ta->module);
+    Py_END_CRITICAL_SECTION();
+    if (module != NULL) {
+        return module;
     }
     if (ta->compute_value != NULL) {
         PyObject* mod = PyFunction_GetModule(ta->compute_value);
@@ -1998,12 +2002,25 @@ typealias_module(PyObject *self, void *Py_UNUSED(closure))
     Py_RETURN_NONE;
 }
 
+static int
+typealias_set_module(PyObject *self, PyObject *value, void *Py_UNUSED(closure))
+{
+    PyObject *old;
+    typealiasobject *ta = typealiasobject_CAST(self);
+    Py_BEGIN_CRITICAL_SECTION(self);
+    old = ta->module;
+    ta->module = Py_XNewRef(value);
+    Py_END_CRITICAL_SECTION();
+    Py_XDECREF(old);
+    return 0;
+}
+
 static PyGetSetDef typealias_getset[] = {
     {"__parameters__", typealias_parameters, NULL, NULL, NULL},
     {"__type_params__", typealias_type_params, NULL, NULL, NULL},
     {"__value__", typealias_value, NULL, NULL, NULL},
     {"evaluate_value", typealias_evaluate_value, NULL, NULL, NULL},
-    {"__module__", typealias_module, NULL, NULL, NULL},
+    {"__module__", typealias_module, typealias_set_module, NULL, NULL},
     {0}
 };
 
@@ -2203,7 +2220,9 @@ type checkers.\n\
 At runtime, Alias is an instance of TypeAliasType. The __name__\n\
 attribute holds the name of the type alias. The value of the type alias\n\
 is stored in the __value__ attribute. It is evaluated lazily, so the\n\
-value is computed only if the attribute is accessed.\n\
+value is computed only if the attribute is accessed. The __module__\n\
+attribute holds the name of the module in which the type alias was\n\
+defined; it can be assigned to.\n\
 \n\
 Type aliases can also be generic::\n\
 \n\