Skip to content

Commit b9dce3a

Browse files
gh-104549: Set __module__ on TypeAliasType (#104550)
1 parent 1c55e8d commit b9dce3a

File tree

9 files changed

+93
-34
lines changed

9 files changed

+93
-34
lines changed

Include/internal/pycore_global_objects.h

-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ struct _Py_interp_cached_objects {
7575
PyTypeObject *paramspec_type;
7676
PyTypeObject *paramspecargs_type;
7777
PyTypeObject *paramspeckwargs_type;
78-
PyTypeObject *typealias_type;
7978
};
8079

8180
#define _Py_INTERP_STATIC_OBJECT(interp, NAME) \

Include/internal/pycore_typevarobject.h

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ extern PyObject *_Py_subscript_generic(PyThreadState *, PyObject *);
1616
extern int _Py_initialize_generic(PyInterpreterState *);
1717
extern void _Py_clear_generic_types(PyInterpreterState *);
1818

19+
extern PyTypeObject _PyTypeAlias_Type;
20+
1921
#ifdef __cplusplus
2022
}
2123
#endif

Lib/test/mod_generics_cache.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Module for testing the behavior of generics across different modules."""
22

3-
from typing import TypeVar, Generic, Optional
3+
from typing import TypeVar, Generic, Optional, TypeAliasType
44

55
default_a: Optional['A'] = None
66
default_b: Optional['B'] = None
@@ -19,3 +19,6 @@ class A(Generic[T]):
1919
my_inner_a1: 'B.A'
2020
my_inner_a2: A
2121
my_outer_a: 'A' # unless somebody calls get_type_hints with localns=B.__dict__
22+
23+
type Alias = int
24+
OldStyle = TypeAliasType("OldStyle", int)

Lib/test/test_type_aliases.py

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import pickle
12
import types
23
import unittest
34
from test.support import check_syntax_error, run_code
5+
from test import mod_generics_cache
46

57
from typing import Callable, TypeAliasType, TypeVar, get_args
68

@@ -155,19 +157,22 @@ def test_basic(self):
155157
self.assertEqual(TA.__name__, "TA")
156158
self.assertIs(TA.__value__, int)
157159
self.assertEqual(TA.__type_params__, ())
160+
self.assertEqual(TA.__module__, __name__)
158161

159162
def test_generic(self):
160163
T = TypeVar("T")
161164
TA = TypeAliasType("TA", list[T], type_params=(T,))
162165
self.assertEqual(TA.__name__, "TA")
163166
self.assertEqual(TA.__value__, list[T])
164167
self.assertEqual(TA.__type_params__, (T,))
168+
self.assertEqual(TA.__module__, __name__)
165169

166170
def test_keywords(self):
167171
TA = TypeAliasType(name="TA", value=int)
168172
self.assertEqual(TA.__name__, "TA")
169173
self.assertIs(TA.__value__, int)
170174
self.assertEqual(TA.__type_params__, ())
175+
self.assertEqual(TA.__module__, __name__)
171176

172177
def test_errors(self):
173178
with self.assertRaises(TypeError):
@@ -202,3 +207,18 @@ def test_union(self):
202207
union3 = list[range] | Alias1
203208
self.assertIsInstance(union3, types.UnionType)
204209
self.assertEqual(get_args(union3), (list[range], Alias1))
210+
211+
def test_module(self):
212+
self.assertEqual(TypeAliasType.__module__, "typing")
213+
type Alias = int
214+
self.assertEqual(Alias.__module__, __name__)
215+
self.assertEqual(mod_generics_cache.Alias.__module__,
216+
mod_generics_cache.__name__)
217+
self.assertEqual(mod_generics_cache.OldStyle.__module__,
218+
mod_generics_cache.__name__)
219+
220+
def test_pickling(self):
221+
pickled = pickle.dumps(mod_generics_cache.Alias)
222+
self.assertIs(pickle.loads(pickled), mod_generics_cache.Alias)
223+
pickled = pickle.dumps(mod_generics_cache.OldStyle)
224+
self.assertIs(pickle.loads(pickled), mod_generics_cache.OldStyle)

Modules/_typingmodule.c

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include "Python.h"
88
#include "internal/pycore_interp.h"
9+
#include "internal/pycore_typevarobject.h"
910
#include "clinic/_typingmodule.c.h"
1011

1112
/*[clinic input]
@@ -56,9 +57,11 @@ _typing_exec(PyObject *m)
5657
EXPORT_TYPE("ParamSpec", paramspec_type);
5758
EXPORT_TYPE("ParamSpecArgs", paramspecargs_type);
5859
EXPORT_TYPE("ParamSpecKwargs", paramspeckwargs_type);
59-
EXPORT_TYPE("TypeAliasType", typealias_type);
6060
EXPORT_TYPE("Generic", generic_type);
6161
#undef EXPORT_TYPE
62+
if (PyModule_AddObjectRef(m, "TypeAliasType", (PyObject *)&_PyTypeAlias_Type) < 0) {
63+
return -1;
64+
}
6265
return 0;
6366
}
6467

Objects/object.c

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
#include "pycore_pymem.h" // _PyMem_IsPtrFreed()
1515
#include "pycore_pystate.h" // _PyThreadState_GET()
1616
#include "pycore_symtable.h" // PySTEntry_Type
17-
#include "pycore_typevarobject.h" // _PyTypeVar_Type etc., _Py_initialize_generic
17+
#include "pycore_typevarobject.h" // _PyTypeAlias_Type, _Py_initialize_generic
1818
#include "pycore_typeobject.h" // _PyBufferWrapper_Type
1919
#include "pycore_unionobject.h" // _PyUnion_Type
2020
#include "pycore_interpreteridobject.h" // _PyInterpreterID_Type
@@ -2112,6 +2112,7 @@ static PyTypeObject* static_types[] = {
21122112
&_PyWeakref_CallableProxyType,
21132113
&_PyWeakref_ProxyType,
21142114
&_PyWeakref_RefType,
2115+
&_PyTypeAlias_Type,
21152116

21162117
// subclasses: _PyTypes_FiniTypes() deallocates them before their base
21172118
// class

Objects/typevarobject.c

+56-27
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ typedef struct {
4848
PyObject *type_params;
4949
PyObject *compute_value;
5050
PyObject *value;
51+
PyObject *module;
5152
} typealiasobject;
5253

5354
#include "clinic/typevarobject.c.h"
@@ -1252,6 +1253,7 @@ typealias_dealloc(PyObject *self)
12521253
Py_XDECREF(ta->type_params);
12531254
Py_XDECREF(ta->compute_value);
12541255
Py_XDECREF(ta->value);
1256+
Py_XDECREF(ta->module);
12551257
Py_TYPE(self)->tp_free(self);
12561258
Py_DECREF(tp);
12571259
}
@@ -1309,26 +1311,41 @@ typealias_type_params(PyObject *self, void *unused)
13091311
return Py_NewRef(ta->type_params);
13101312
}
13111313

1314+
static PyObject *
1315+
typealias_module(PyObject *self, void *unused)
1316+
{
1317+
typealiasobject *ta = (typealiasobject *)self;
1318+
if (ta->module != NULL) {
1319+
return Py_NewRef(ta->module);
1320+
}
1321+
if (ta->compute_value != NULL) {
1322+
// PyFunction_GetModule() returns a borrowed reference
1323+
return Py_NewRef(PyFunction_GetModule(ta->compute_value));
1324+
}
1325+
Py_RETURN_NONE;
1326+
}
1327+
13121328
static PyGetSetDef typealias_getset[] = {
13131329
{"__parameters__", typealias_parameters, (setter)NULL, NULL, NULL},
13141330
{"__type_params__", typealias_type_params, (setter)NULL, NULL, NULL},
13151331
{"__value__", typealias_value, (setter)NULL, NULL, NULL},
1332+
{"__module__", typealias_module, (setter)NULL, NULL, NULL},
13161333
{0}
13171334
};
13181335

13191336
static typealiasobject *
13201337
typealias_alloc(PyObject *name, PyObject *type_params, PyObject *compute_value,
1321-
PyObject *value)
1338+
PyObject *value, PyObject *module)
13221339
{
1323-
PyTypeObject *tp = PyInterpreterState_Get()->cached_objects.typealias_type;
1324-
typealiasobject *ta = PyObject_GC_New(typealiasobject, tp);
1340+
typealiasobject *ta = PyObject_GC_New(typealiasobject, &_PyTypeAlias_Type);
13251341
if (ta == NULL) {
13261342
return NULL;
13271343
}
13281344
ta->name = Py_NewRef(name);
13291345
ta->type_params = Py_IsNone(type_params) ? NULL : Py_XNewRef(type_params);
13301346
ta->compute_value = Py_XNewRef(compute_value);
13311347
ta->value = Py_XNewRef(value);
1348+
ta->module = Py_XNewRef(module);
13321349
_PyObject_GC_TRACK(ta);
13331350
return ta;
13341351
}
@@ -1339,6 +1356,7 @@ typealias_traverse(typealiasobject *self, visitproc visit, void *arg)
13391356
Py_VISIT(self->type_params);
13401357
Py_VISIT(self->compute_value);
13411358
Py_VISIT(self->value);
1359+
Py_VISIT(self->module);
13421360
return 0;
13431361
}
13441362

@@ -1348,6 +1366,7 @@ typealias_clear(typealiasobject *self)
13481366
Py_CLEAR(self->type_params);
13491367
Py_CLEAR(self->compute_value);
13501368
Py_CLEAR(self->value);
1369+
Py_CLEAR(self->module);
13511370
return 0;
13521371
}
13531372

@@ -1401,7 +1420,14 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value,
14011420
PyErr_SetString(PyExc_TypeError, "type_params must be a tuple");
14021421
return NULL;
14031422
}
1404-
return (PyObject *)typealias_alloc(name, type_params, NULL, value);
1423+
PyObject *module = caller();
1424+
if (module == NULL) {
1425+
return NULL;
1426+
}
1427+
PyObject *ta = (PyObject *)typealias_alloc(name, type_params, NULL, value,
1428+
module);
1429+
Py_DECREF(module);
1430+
return ta;
14051431
}
14061432

14071433
PyDoc_STRVAR(typealias_doc,
@@ -1412,28 +1438,32 @@ Type aliases are created through the type statement:\n\
14121438
type Alias = int\n\
14131439
");
14141440

1415-
static PyType_Slot typealias_slots[] = {
1416-
{Py_tp_doc, (void *)typealias_doc},
1417-
{Py_tp_members, typealias_members},
1418-
{Py_tp_methods, typealias_methods},
1419-
{Py_tp_getset, typealias_getset},
1420-
{Py_mp_subscript, typealias_subscript},
1421-
{Py_tp_dealloc, typealias_dealloc},
1422-
{Py_tp_alloc, PyType_GenericAlloc},
1423-
{Py_tp_new, typealias_new},
1424-
{Py_tp_free, PyObject_GC_Del},
1425-
{Py_tp_traverse, (traverseproc)typealias_traverse},
1426-
{Py_tp_clear, (inquiry)typealias_clear},
1427-
{Py_tp_repr, typealias_repr},
1428-
{Py_nb_or, _Py_union_type_or},
1429-
{0, 0},
1441+
static PyNumberMethods typealias_as_number = {
1442+
.nb_or = _Py_union_type_or,
1443+
};
1444+
1445+
static PyMappingMethods typealias_as_mapping = {
1446+
.mp_subscript = typealias_subscript,
14301447
};
14311448

1432-
PyType_Spec typealias_spec = {
1433-
.name = "typing.TypeAliasType",
1434-
.basicsize = sizeof(typealiasobject),
1435-
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC,
1436-
.slots = typealias_slots,
1449+
PyTypeObject _PyTypeAlias_Type = {
1450+
PyVarObject_HEAD_INIT(&PyType_Type, 0)
1451+
.tp_name = "typing.TypeAliasType",
1452+
.tp_basicsize = sizeof(typealiasobject),
1453+
.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE | Py_TPFLAGS_HAVE_GC,
1454+
.tp_doc = typealias_doc,
1455+
.tp_members = typealias_members,
1456+
.tp_methods = typealias_methods,
1457+
.tp_getset = typealias_getset,
1458+
.tp_alloc = PyType_GenericAlloc,
1459+
.tp_dealloc = typealias_dealloc,
1460+
.tp_new = typealias_new,
1461+
.tp_free = PyObject_GC_Del,
1462+
.tp_traverse = (traverseproc)typealias_traverse,
1463+
.tp_clear = (inquiry)typealias_clear,
1464+
.tp_repr = typealias_repr,
1465+
.tp_as_number = &typealias_as_number,
1466+
.tp_as_mapping = &typealias_as_mapping,
14371467
};
14381468

14391469
PyObject *
@@ -1445,7 +1475,8 @@ _Py_make_typealias(PyThreadState* unused, PyObject *args)
14451475
assert(PyUnicode_Check(name));
14461476
PyObject *type_params = PyTuple_GET_ITEM(args, 1);
14471477
PyObject *compute_value = PyTuple_GET_ITEM(args, 2);
1448-
return (PyObject *)typealias_alloc(name, type_params, compute_value, NULL);
1478+
assert(PyFunction_Check(compute_value));
1479+
return (PyObject *)typealias_alloc(name, type_params, compute_value, NULL, NULL);
14491480
}
14501481

14511482
PyDoc_STRVAR(generic_doc,
@@ -1603,7 +1634,6 @@ int _Py_initialize_generic(PyInterpreterState *interp)
16031634
MAKE_TYPE(paramspec);
16041635
MAKE_TYPE(paramspecargs);
16051636
MAKE_TYPE(paramspeckwargs);
1606-
MAKE_TYPE(typealias);
16071637
#undef MAKE_TYPE
16081638
return 0;
16091639
}
@@ -1616,5 +1646,4 @@ void _Py_clear_generic_types(PyInterpreterState *interp)
16161646
Py_CLEAR(interp->cached_objects.paramspec_type);
16171647
Py_CLEAR(interp->cached_objects.paramspecargs_type);
16181648
Py_CLEAR(interp->cached_objects.paramspeckwargs_type);
1619-
Py_CLEAR(interp->cached_objects.typealias_type);
16201649
}

Objects/unionobject.c

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// types.UnionType -- used to represent e.g. Union[int, str], int | str
22
#include "Python.h"
33
#include "pycore_object.h" // _PyObject_GC_TRACK/UNTRACK
4+
#include "pycore_typevarobject.h" // _PyTypeAlias_Type
45
#include "pycore_unionobject.h"
56
#include "structmember.h"
67

@@ -150,11 +151,11 @@ is_unionable(PyObject *obj)
150151
if (obj == Py_None ||
151152
PyType_Check(obj) ||
152153
_PyGenericAlias_Check(obj) ||
153-
_PyUnion_Check(obj)) {
154+
_PyUnion_Check(obj) ||
155+
Py_IS_TYPE(obj, &_PyTypeAlias_Type)) {
154156
return 1;
155157
}
156-
PyInterpreterState *interp = PyInterpreterState_Get();
157-
return Py_IS_TYPE(obj, interp->cached_objects.typealias_type);
158+
return 0;
158159
}
159160

160161
PyObject *

Tools/c-analyzer/cpython/globals-to-fix.tsv

+1
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Objects/typeobject.c - _PyBufferWrapper_Type -
9090
Objects/typeobject.c - PyBaseObject_Type -
9191
Objects/typeobject.c - PySuper_Type -
9292
Objects/typeobject.c - PyType_Type -
93+
Objects/typevarobject.c - _PyTypeAlias_Type -
9394
Objects/unicodeobject.c - PyUnicodeIter_Type -
9495
Objects/unicodeobject.c - PyUnicode_Type -
9596
Objects/weakrefobject.c - _PyWeakref_CallableProxyType -

0 commit comments

Comments
 (0)