Skip to content

Commit 3fb63a3

Browse files
committed
Merge remote-tracking branch 'upstream/main' into concatenate/ellipsis
2 parents de6a2c6 + 832253d commit 3fb63a3

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
@@ -7319,6 +7319,29 @@ def test_getitem(self):
73197319
self.assertEqual(get_args(fully_subscripted), (Iterable[float],))
73207320
self.assertIs(get_origin(fully_subscripted), ListOrSetT)
73217321

7322+
def test_alias_attributes(self):
7323+
T = TypeVar('T')
7324+
T2 = TypeVar('T2')
7325+
ListOrSetT = TypeAliasType("ListOrSetT", Union[List[T], Set[T]], type_params=(T,))
7326+
7327+
subscripted = ListOrSetT[int]
7328+
self.assertEqual(subscripted.__module__, ListOrSetT.__module__)
7329+
self.assertEqual(subscripted.__name__, "ListOrSetT")
7330+
self.assertEqual(subscripted.__value__, Union[List[T], Set[T]])
7331+
self.assertEqual(subscripted.__type_params__, (T,))
7332+
7333+
still_generic = ListOrSetT[Iterable[T2]]
7334+
self.assertEqual(still_generic.__module__, ListOrSetT.__module__)
7335+
self.assertEqual(still_generic.__name__, "ListOrSetT")
7336+
self.assertEqual(still_generic.__value__, Union[List[T], Set[T]])
7337+
self.assertEqual(still_generic.__type_params__, (T,))
7338+
7339+
fully_subscripted = still_generic[float]
7340+
self.assertEqual(fully_subscripted.__module__, ListOrSetT.__module__)
7341+
self.assertEqual(fully_subscripted.__name__, "ListOrSetT")
7342+
self.assertEqual(fully_subscripted.__value__, Union[List[T], Set[T]])
7343+
self.assertEqual(fully_subscripted.__type_params__, (T,))
7344+
73227345
def test_subscription_without_type_params(self):
73237346
Simple = TypeAliasType("Simple", int)
73247347
with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"):
@@ -7332,7 +7355,6 @@ def test_subscription_without_type_params(self):
73327355
with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"):
73337356
MissingTypeParamsErr[int]
73347357

7335-
73367358
def test_pickle(self):
73377359
global Alias
73387360
Alias = TypeAliasType("Alias", int)

src/typing_extensions.py

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3486,6 +3486,37 @@ def _is_unionable(obj):
34863486
TypeAliasType,
34873487
))
34883488

3489+
if sys.version_info < (3, 10):
3490+
# Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582,
3491+
# so that we emulate the behaviour of `types.GenericAlias`
3492+
# on the latest versions of CPython
3493+
_ATTRIBUTE_DELEGATION_EXCLUSIONS = frozenset({
3494+
"__class__",
3495+
"__bases__",
3496+
"__origin__",
3497+
"__args__",
3498+
"__unpacked__",
3499+
"__parameters__",
3500+
"__typing_unpacked_tuple_args__",
3501+
"__mro_entries__",
3502+
"__reduce_ex__",
3503+
"__reduce__",
3504+
"__copy__",
3505+
"__deepcopy__",
3506+
})
3507+
3508+
class _TypeAliasGenericAlias(typing._GenericAlias, _root=True):
3509+
def __getattr__(self, attr):
3510+
if attr in _ATTRIBUTE_DELEGATION_EXCLUSIONS:
3511+
return object.__getattr__(self, attr)
3512+
return getattr(self.__origin__, attr)
3513+
3514+
if sys.version_info < (3, 9):
3515+
def __getitem__(self, item):
3516+
result = super().__getitem__(item)
3517+
result.__class__ = type(self)
3518+
return result
3519+
34893520
class TypeAliasType:
34903521
"""Create named, parameterized type aliases.
34913522
@@ -3563,13 +3594,16 @@ def __getitem__(self, parameters):
35633594
raise TypeError("Only generic type aliases are subscriptable")
35643595
if not isinstance(parameters, tuple):
35653596
parameters = (parameters,)
3566-
parameters = [
3597+
# Using 3.9 here will create problems with Concatenate
3598+
if sys.version_info >= (3, 10):
3599+
return _types.GenericAlias(self, parameters)
3600+
parameters = tuple(
35673601
typing._type_check(
35683602
item, f'Subscripting {self.__name__} requires a type.'
35693603
)
35703604
for item in parameters
3571-
]
3572-
return typing._GenericAlias(self, tuple(parameters))
3605+
)
3606+
return _TypeAliasGenericAlias(self, parameters)
35733607

35743608
def __reduce__(self):
35753609
return self.__name__

0 commit comments

Comments
 (0)