Skip to content

Commit 832253d

Browse files
DaraanAlexWaygood
andauthored
Add missing dunder attributes for TypeAliasType instances (#470)
Co-authored-by: Alex Waygood <[email protected]>
1 parent 2c84de1 commit 832253d

File tree

3 files changed

+63
-4
lines changed

3 files changed

+63
-4
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
with `@typing_extensions.deprecated`. Patch by Sebastian Rittau.
1010
- Fix bug where `TypeAliasType` instances could be subscripted even
1111
where they were not generic. Patch by [Daraan](https://github.com/Daraan).
12+
- Fix bug where a subscripted `TypeAliasType` instance did not have all
13+
attributes of the original `TypeAliasType` instance on older Python versions.
14+
Patch by [Daraan](https://github.com/Daraan) and Alex Waygood.
1215

1316
# Release 4.12.2 (June 7, 2024)
1417

src/test_typing_extensions.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7247,6 +7247,29 @@ def test_getitem(self):
72477247
self.assertEqual(get_args(fully_subscripted), (Iterable[float],))
72487248
self.assertIs(get_origin(fully_subscripted), ListOrSetT)
72497249

7250+
def test_alias_attributes(self):
7251+
T = TypeVar('T')
7252+
T2 = TypeVar('T2')
7253+
ListOrSetT = TypeAliasType("ListOrSetT", Union[List[T], Set[T]], type_params=(T,))
7254+
7255+
subscripted = ListOrSetT[int]
7256+
self.assertEqual(subscripted.__module__, ListOrSetT.__module__)
7257+
self.assertEqual(subscripted.__name__, "ListOrSetT")
7258+
self.assertEqual(subscripted.__value__, Union[List[T], Set[T]])
7259+
self.assertEqual(subscripted.__type_params__, (T,))
7260+
7261+
still_generic = ListOrSetT[Iterable[T2]]
7262+
self.assertEqual(still_generic.__module__, ListOrSetT.__module__)
7263+
self.assertEqual(still_generic.__name__, "ListOrSetT")
7264+
self.assertEqual(still_generic.__value__, Union[List[T], Set[T]])
7265+
self.assertEqual(still_generic.__type_params__, (T,))
7266+
7267+
fully_subscripted = still_generic[float]
7268+
self.assertEqual(fully_subscripted.__module__, ListOrSetT.__module__)
7269+
self.assertEqual(fully_subscripted.__name__, "ListOrSetT")
7270+
self.assertEqual(fully_subscripted.__value__, Union[List[T], Set[T]])
7271+
self.assertEqual(fully_subscripted.__type_params__, (T,))
7272+
72507273
def test_subscription_without_type_params(self):
72517274
Simple = TypeAliasType("Simple", int)
72527275
with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"):
@@ -7260,7 +7283,6 @@ def test_subscription_without_type_params(self):
72607283
with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"):
72617284
MissingTypeParamsErr[int]
72627285

7263-
72647286
def test_pickle(self):
72657287
global Alias
72667288
Alias = TypeAliasType("Alias", int)

src/typing_extensions.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3452,6 +3452,37 @@ def _is_unionable(obj):
34523452
TypeAliasType,
34533453
))
34543454

3455+
if sys.version_info < (3, 10):
3456+
# Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582,
3457+
# so that we emulate the behaviour of `types.GenericAlias`
3458+
# on the latest versions of CPython
3459+
_ATTRIBUTE_DELEGATION_EXCLUSIONS = frozenset({
3460+
"__class__",
3461+
"__bases__",
3462+
"__origin__",
3463+
"__args__",
3464+
"__unpacked__",
3465+
"__parameters__",
3466+
"__typing_unpacked_tuple_args__",
3467+
"__mro_entries__",
3468+
"__reduce_ex__",
3469+
"__reduce__",
3470+
"__copy__",
3471+
"__deepcopy__",
3472+
})
3473+
3474+
class _TypeAliasGenericAlias(typing._GenericAlias, _root=True):
3475+
def __getattr__(self, attr):
3476+
if attr in _ATTRIBUTE_DELEGATION_EXCLUSIONS:
3477+
return object.__getattr__(self, attr)
3478+
return getattr(self.__origin__, attr)
3479+
3480+
if sys.version_info < (3, 9):
3481+
def __getitem__(self, item):
3482+
result = super().__getitem__(item)
3483+
result.__class__ = type(self)
3484+
return result
3485+
34553486
class TypeAliasType:
34563487
"""Create named, parameterized type aliases.
34573488
@@ -3529,13 +3560,16 @@ def __getitem__(self, parameters):
35293560
raise TypeError("Only generic type aliases are subscriptable")
35303561
if not isinstance(parameters, tuple):
35313562
parameters = (parameters,)
3532-
parameters = [
3563+
# Using 3.9 here will create problems with Concatenate
3564+
if sys.version_info >= (3, 10):
3565+
return _types.GenericAlias(self, parameters)
3566+
parameters = tuple(
35333567
typing._type_check(
35343568
item, f'Subscripting {self.__name__} requires a type.'
35353569
)
35363570
for item in parameters
3537-
]
3538-
return typing._GenericAlias(self, tuple(parameters))
3571+
)
3572+
return _TypeAliasGenericAlias(self, parameters)
35393573

35403574
def __reduce__(self):
35413575
return self.__name__

0 commit comments

Comments
 (0)