Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 832253d

Browse filesBrowse files
DaraanAlexWaygood
andauthored
Add missing dunder attributes for TypeAliasType instances (#470)
Co-authored-by: Alex Waygood <Alex.Waygood@Gmail.com>
1 parent 2c84de1 commit 832253d
Copy full SHA for 832253d

File tree

Expand file treeCollapse file tree

3 files changed

+63
-4
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+63
-4
lines changed

‎CHANGELOG.md

Copy file name to clipboardExpand all lines: CHANGELOG.md
+3Lines 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

Copy file name to clipboardExpand all lines: src/test_typing_extensions.py
+23-1Lines 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

Copy file name to clipboardExpand all lines: src/typing_extensions.py
+37-3Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.