Skip to content
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
with `@typing_extensions.deprecated`. Patch by Sebastian Rittau.
- Fix bug where `TypeAliasType` instances could be subscripted even
where they were not generic. Patch by [Daraan](https://github.com/Daraan).
- Fix bug where a subscripted `TypeAliasType` instance did not have all
attributes of the original `TypeAliasType` instance on older Python versions.
Patch by [Daraan](https://github.com/Daraan) and Alex Waygood.

# Release 4.12.2 (June 7, 2024)

Expand Down
24 changes: 23 additions & 1 deletion src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7247,6 +7247,29 @@ def test_getitem(self):
self.assertEqual(get_args(fully_subscripted), (Iterable[float],))
self.assertIs(get_origin(fully_subscripted), ListOrSetT)

def test_alias_attributes(self):
T = TypeVar('T')
T2 = TypeVar('T2')
ListOrSetT = TypeAliasType("ListOrSetT", Union[List[T], Set[T]], type_params=(T,))

subscripted = ListOrSetT[int]
self.assertEqual(subscripted.__module__, ListOrSetT.__module__)
self.assertEqual(subscripted.__name__, "ListOrSetT")
self.assertEqual(subscripted.__value__, Union[List[T], Set[T]])
self.assertEqual(subscripted.__type_params__, (T,))

still_generic = ListOrSetT[Iterable[T2]]
self.assertEqual(still_generic.__module__, ListOrSetT.__module__)
self.assertEqual(still_generic.__name__, "ListOrSetT")
self.assertEqual(still_generic.__value__, Union[List[T], Set[T]])
self.assertEqual(still_generic.__type_params__, (T,))

fully_subscripted = still_generic[float]
self.assertEqual(fully_subscripted.__module__, ListOrSetT.__module__)
self.assertEqual(fully_subscripted.__name__, "ListOrSetT")
self.assertEqual(fully_subscripted.__value__, Union[List[T], Set[T]])
self.assertEqual(fully_subscripted.__type_params__, (T,))

def test_subscription_without_type_params(self):
Simple = TypeAliasType("Simple", int)
with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"):
Expand All @@ -7260,7 +7283,6 @@ def test_subscription_without_type_params(self):
with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"):
MissingTypeParamsErr[int]


def test_pickle(self):
global Alias
Alias = TypeAliasType("Alias", int)
Expand Down
22 changes: 19 additions & 3 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3452,6 +3452,19 @@ def _is_unionable(obj):
TypeAliasType,
))

if sys.version_info < (3, 10):
class _TypeAliasGenericAlias(typing._GenericAlias, _root=True):
def __getattr__(self, attr):
if attr in {"__value__", "__type_params__", "__name__"}:
return getattr(self.__origin__, attr)
return super().__getattr__(attr)

if sys.version_info < (3, 9):
def __getitem__(self, item):
result = super().__getitem__(item)
result.__class__ = type(self)
return result

class TypeAliasType:
"""Create named, parameterized type aliases.

Expand Down Expand Up @@ -3529,13 +3542,16 @@ def __getitem__(self, parameters):
raise TypeError("Only generic type aliases are subscriptable")
if not isinstance(parameters, tuple):
parameters = (parameters,)
parameters = [
# Using 3.9 here will create problems with Concatenate
if sys.version_info >= (3, 10):
return _types.GenericAlias(self, parameters)
parameters = tuple(
typing._type_check(
item, f'Subscripting {self.__name__} requires a type.'
)
for item in parameters
]
return typing._GenericAlias(self, tuple(parameters))
)
return _TypeAliasGenericAlias(self, parameters)

def __reduce__(self):
return self.__name__
Expand Down
Loading