Skip to content

Commit f673f0e

Browse files
gh-139817: Attribute __qualname__ is added to TypeAliasType (#139919)
1 parent 5213f1b commit f673f0e

File tree

9 files changed

+144
-33
lines changed

9 files changed

+144
-33
lines changed

Doc/library/typing.rst

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2243,7 +2243,7 @@ without the dedicated syntax, as documented below.
22432243
.. versionadded:: 3.10
22442244

22452245

2246-
.. class:: TypeAliasType(name, value, *, type_params=())
2246+
.. class:: TypeAliasType(name, value, *, type_params=(), qualname=None)
22472247

22482248
The type of type aliases created through the :keyword:`type` statement.
22492249

@@ -2267,6 +2267,20 @@ without the dedicated syntax, as documented below.
22672267
>>> Alias.__name__
22682268
'Alias'
22692269

2270+
.. attribute:: __qualname__
2271+
2272+
The :term:`qualified name` of the type alias:
2273+
2274+
.. doctest::
2275+
2276+
>>> class Class:
2277+
... type Alias = int
2278+
...
2279+
>>> Class.Alias.__qualname__
2280+
'Class.Alias'
2281+
2282+
.. versionadded:: 3.15
2283+
22702284
.. attribute:: __module__
22712285

22722286
The name of the module in which the type alias was defined::

Include/internal/pycore_global_objects_fini_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_global_strings.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -697,6 +697,7 @@ struct _Py_global_strings {
697697
STRUCT_FOR_ID(ps1)
698698
STRUCT_FOR_ID(ps2)
699699
STRUCT_FOR_ID(qid)
700+
STRUCT_FOR_ID(qualname)
700701
STRUCT_FOR_ID(query)
701702
STRUCT_FOR_ID(queuetype)
702703
STRUCT_FOR_ID(quotetabs)

Include/internal/pycore_runtime_init_generated.h

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Include/internal/pycore_unicodeobject_generated.h

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Lib/test/test_type_aliases.py

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@
88
Callable, TypeAliasType, TypeVar, TypeVarTuple, ParamSpec, Unpack, get_args,
99
)
1010

11+
type GlobalTypeAlias = int
12+
13+
def get_type_alias():
14+
type TypeAliasInFunc = str
15+
return TypeAliasInFunc
1116

1217
class TypeParamsInvalidTest(unittest.TestCase):
1318
def test_name_collisions(self):
@@ -70,6 +75,8 @@ def inner[B](self):
7075

7176

7277
class TypeParamsAliasValueTest(unittest.TestCase):
78+
type TypeAliasInClass = dict
79+
7380
def test_alias_value_01(self):
7481
type TA1 = int
7582

@@ -142,33 +149,67 @@ def test_subscripting(self):
142149
self.assertIs(specialized2.__origin__, VeryGeneric)
143150
self.assertEqual(specialized2.__args__, (int, str, float, [bool, range]))
144151

152+
def test___name__(self):
153+
type TypeAliasLocal = GlobalTypeAlias
154+
155+
self.assertEqual(GlobalTypeAlias.__name__, 'GlobalTypeAlias')
156+
self.assertEqual(get_type_alias().__name__, 'TypeAliasInFunc')
157+
self.assertEqual(self.TypeAliasInClass.__name__, 'TypeAliasInClass')
158+
self.assertEqual(TypeAliasLocal.__name__, 'TypeAliasLocal')
159+
160+
with self.assertRaisesRegex(
161+
AttributeError,
162+
"readonly attribute",
163+
):
164+
setattr(TypeAliasLocal, '__name__', 'TA')
165+
166+
def test___qualname__(self):
167+
type TypeAliasLocal = GlobalTypeAlias
168+
169+
self.assertEqual(GlobalTypeAlias.__qualname__,
170+
'GlobalTypeAlias')
171+
self.assertEqual(get_type_alias().__qualname__,
172+
'get_type_alias.<locals>.TypeAliasInFunc')
173+
self.assertEqual(self.TypeAliasInClass.__qualname__,
174+
'TypeParamsAliasValueTest.TypeAliasInClass')
175+
self.assertEqual(TypeAliasLocal.__qualname__,
176+
'TypeParamsAliasValueTest.test___qualname__.<locals>.TypeAliasLocal')
177+
178+
with self.assertRaisesRegex(
179+
AttributeError,
180+
"readonly attribute",
181+
):
182+
setattr(TypeAliasLocal, '__qualname__', 'TA')
183+
145184
def test_repr(self):
146185
type Simple = int
147-
type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]]
186+
self.assertEqual(repr(Simple), Simple.__qualname__)
148187

149-
self.assertEqual(repr(Simple), "Simple")
150-
self.assertEqual(repr(VeryGeneric), "VeryGeneric")
188+
type VeryGeneric[T, *Ts, **P] = Callable[P, tuple[T, *Ts]]
189+
self.assertEqual(repr(VeryGeneric), VeryGeneric.__qualname__)
190+
fullname = f"{VeryGeneric.__module__}.{VeryGeneric.__qualname__}"
151191
self.assertEqual(repr(VeryGeneric[int, bytes, str, [float, object]]),
152-
"VeryGeneric[int, bytes, str, [float, object]]")
192+
f"{fullname}[int, bytes, str, [float, object]]")
153193
self.assertEqual(repr(VeryGeneric[int, []]),
154-
"VeryGeneric[int, []]")
194+
f"{fullname}[int, []]")
155195
self.assertEqual(repr(VeryGeneric[int, [VeryGeneric[int], list[str]]]),
156-
"VeryGeneric[int, [VeryGeneric[int], list[str]]]")
196+
f"{fullname}[int, [{fullname}[int], list[str]]]")
157197

158198
def test_recursive_repr(self):
159199
type Recursive = Recursive
160-
self.assertEqual(repr(Recursive), "Recursive")
200+
self.assertEqual(repr(Recursive), Recursive.__qualname__)
161201

162202
type X = list[Y]
163203
type Y = list[X]
164-
self.assertEqual(repr(X), "X")
165-
self.assertEqual(repr(Y), "Y")
204+
self.assertEqual(repr(X), X.__qualname__)
205+
self.assertEqual(repr(Y), Y.__qualname__)
166206

167207
type GenericRecursive[X] = list[X | GenericRecursive[X]]
168-
self.assertEqual(repr(GenericRecursive), "GenericRecursive")
169-
self.assertEqual(repr(GenericRecursive[int]), "GenericRecursive[int]")
208+
self.assertEqual(repr(GenericRecursive), GenericRecursive.__qualname__)
209+
fullname = f"{GenericRecursive.__module__}.{GenericRecursive.__qualname__}"
210+
self.assertEqual(repr(GenericRecursive[int]), f"{fullname}[int]")
170211
self.assertEqual(repr(GenericRecursive[GenericRecursive[int]]),
171-
"GenericRecursive[GenericRecursive[int]]")
212+
f"{fullname}[{fullname}[int]]")
172213

173214
def test_raising(self):
174215
type MissingName = list[_My_X]
@@ -193,15 +234,25 @@ class TypeAliasConstructorTest(unittest.TestCase):
193234
def test_basic(self):
194235
TA = TypeAliasType("TA", int)
195236
self.assertEqual(TA.__name__, "TA")
237+
self.assertEqual(TA.__qualname__, "TA")
196238
self.assertIs(TA.__value__, int)
197239
self.assertEqual(TA.__type_params__, ())
198240
self.assertEqual(TA.__module__, __name__)
199241

242+
def test_with_qualname(self):
243+
TA = TypeAliasType("TA", str, qualname="Class.TA")
244+
self.assertEqual(TA.__name__, "TA")
245+
self.assertEqual(TA.__qualname__, "Class.TA")
246+
self.assertIs(TA.__value__, str)
247+
self.assertEqual(TA.__type_params__, ())
248+
self.assertEqual(TA.__module__, __name__)
249+
200250
def test_attributes_with_exec(self):
201251
ns = {}
202252
exec("type TA = int", ns, ns)
203253
TA = ns["TA"]
204254
self.assertEqual(TA.__name__, "TA")
255+
self.assertEqual(TA.__qualname__, "TA")
205256
self.assertIs(TA.__value__, int)
206257
self.assertEqual(TA.__type_params__, ())
207258
self.assertIs(TA.__module__, None)
@@ -210,6 +261,7 @@ def test_generic(self):
210261
T = TypeVar("T")
211262
TA = TypeAliasType("TA", list[T], type_params=(T,))
212263
self.assertEqual(TA.__name__, "TA")
264+
self.assertEqual(TA.__qualname__, "TA")
213265
self.assertEqual(TA.__value__, list[T])
214266
self.assertEqual(TA.__type_params__, (T,))
215267
self.assertEqual(TA.__module__, __name__)
@@ -218,6 +270,7 @@ def test_generic(self):
218270
def test_not_generic(self):
219271
TA = TypeAliasType("TA", list[int], type_params=())
220272
self.assertEqual(TA.__name__, "TA")
273+
self.assertEqual(TA.__qualname__, "TA")
221274
self.assertEqual(TA.__value__, list[int])
222275
self.assertEqual(TA.__type_params__, ())
223276
self.assertEqual(TA.__module__, __name__)
@@ -268,8 +321,9 @@ def test_expects_type_like(self):
268321
TypeAliasType("A", int, type_params=(T, 2))
269322

270323
def test_keywords(self):
271-
TA = TypeAliasType(name="TA", value=int)
324+
TA = TypeAliasType(name="TA", value=int, type_params=(), qualname=None)
272325
self.assertEqual(TA.__name__, "TA")
326+
self.assertEqual(TA.__qualname__, "TA")
273327
self.assertIs(TA.__value__, int)
274328
self.assertEqual(TA.__type_params__, ())
275329
self.assertEqual(TA.__module__, __name__)
@@ -283,6 +337,8 @@ def test_errors(self):
283337
TypeAliasType("TA", list, ())
284338
with self.assertRaises(TypeError):
285339
TypeAliasType("TA", list, type_params=42)
340+
with self.assertRaises(TypeError):
341+
TypeAliasType("TA", list, qualname=range(5))
286342

287343

288344
class TypeAliasTypeTest(unittest.TestCase):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Attribute ``__qualname__`` is added to :class:`typing.TypeAliasType`.
2+
Patch by Mikhail Efimov.

Objects/clinic/typevarobject.c.h

Lines changed: 16 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)