Skip to content

Commit

Permalink
Add missing dunder attributes for TypeAliasType instances (#470)
Browse files Browse the repository at this point in the history
Co-authored-by: Alex Waygood <[email protected]>
  • Loading branch information
Daraan and AlexWaygood authored Sep 26, 2024
1 parent 2c84de1 commit 832253d
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 4 deletions.
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
40 changes: 37 additions & 3 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3452,6 +3452,37 @@ def _is_unionable(obj):
TypeAliasType,
))

if sys.version_info < (3, 10):
# Copied and pasted from https://github.com/python/cpython/blob/986a4e1b6fcae7fe7a1d0a26aea446107dd58dd2/Objects/genericaliasobject.c#L568-L582,
# so that we emulate the behaviour of `types.GenericAlias`
# on the latest versions of CPython
_ATTRIBUTE_DELEGATION_EXCLUSIONS = frozenset({
"__class__",
"__bases__",
"__origin__",
"__args__",
"__unpacked__",
"__parameters__",
"__typing_unpacked_tuple_args__",
"__mro_entries__",
"__reduce_ex__",
"__reduce__",
"__copy__",
"__deepcopy__",
})

class _TypeAliasGenericAlias(typing._GenericAlias, _root=True):
def __getattr__(self, attr):
if attr in _ATTRIBUTE_DELEGATION_EXCLUSIONS:
return object.__getattr__(self, attr)
return getattr(self.__origin__, 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 +3560,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

0 comments on commit 832253d

Please sign in to comment.