From 629c57e2248b773733d45c961b6a3e4e7480f79d Mon Sep 17 00:00:00 2001 From: Daniel Date: Tue, 24 Sep 2024 21:03:45 +0200 Subject: [PATCH 1/7] Correct TypeError when type_params are empty. --- src/test_typing_extensions.py | 37 +++++++++++++++++++++++++++++++++++ src/typing_extensions.py | 15 ++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index acd762ee..bb674350 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -7247,6 +7247,43 @@ def test_getitem(self): self.assertEqual(get_args(fully_subscripted), (Iterable[float],)) self.assertIs(get_origin(fully_subscripted), ListOrSetT) + def test_subscription_without_type_params(self): + Simple = TypeAliasType("Simple", int) + with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): + Simple[int] + with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): + Simple[[]] + with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): + Simple[()] + + # A TypeVar in the value does not allow subscription + T = TypeVar('T') + MissingTypeParamsErr = TypeAliasType("MissingTypeParamsErr", List[T]) + self.assertEqual(MissingTypeParamsErr.__type_params__, ()) + self.assertEqual(MissingTypeParamsErr.__parameters__, ()) + with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): + MissingTypeParamsErr[int] + with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): + MissingTypeParamsErr[[]] + with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): + MissingTypeParamsErr[()] + + # However, providing type_params=() argument allows subscription + MissingTypeParams = TypeAliasType("MissingTypeParams", List[T], type_params=()) + self.assertEqual(MissingTypeParams.__type_params__, ()) + self.assertEqual(MissingTypeParams.__parameters__, ()) + # These do not raise + MissingTypeParams[int] + MissingTypeParams[()] + Simple2 = TypeAliasType("Simple2", int, type_params=()) + Simple2[int] + Simple2[()] + with self.subTest(): + if sys.version_info < (3, 11): + self.skipTest("needs change how parameters are checked") + MissingTypeParams[[]] + Simple2[[]] + def test_pickle(self): global Alias Alias = TypeAliasType("Alias", int) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 1adc5823..5947c8b2 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3524,7 +3524,22 @@ def _raise_attribute_error(self, name: str) -> Never: def __repr__(self) -> str: return self.__name__ + def _is_subscriptable(self): + if len(self.__parameters__) > 0: + return True + if _should_collect_from_parameters(self.__value__): + if hasattr(typing, '_collect_type_vars'): + more_parameters = _collect_type_vars((self.__value__,), + (TypeVar, ParamSpec)) + else: + more_parameters = _collect_parameters((self.__value__,)) + if more_parameters: + return True + return False + def __getitem__(self, parameters): + if len(self.__parameters__) == 0 and not self._is_subscriptable(): + raise TypeError("Only generic type aliases are subscriptable") if not isinstance(parameters, tuple): parameters = (parameters,) parameters = [ From 4b0e5d23f3cc501fa88af82c88355021a28a8e82 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 26 Sep 2024 17:50:33 +0200 Subject: [PATCH 2/7] Applied corrections to align with https://github.com/python/cpython/pull/124499 --- src/test_typing_extensions.py | 17 +---------------- src/typing_extensions.py | 15 +-------------- 2 files changed, 2 insertions(+), 30 deletions(-) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index bb674350..c111ee65 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -7267,22 +7267,7 @@ def test_subscription_without_type_params(self): MissingTypeParamsErr[[]] with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): MissingTypeParamsErr[()] - - # However, providing type_params=() argument allows subscription - MissingTypeParams = TypeAliasType("MissingTypeParams", List[T], type_params=()) - self.assertEqual(MissingTypeParams.__type_params__, ()) - self.assertEqual(MissingTypeParams.__parameters__, ()) - # These do not raise - MissingTypeParams[int] - MissingTypeParams[()] - Simple2 = TypeAliasType("Simple2", int, type_params=()) - Simple2[int] - Simple2[()] - with self.subTest(): - if sys.version_info < (3, 11): - self.skipTest("needs change how parameters are checked") - MissingTypeParams[[]] - Simple2[[]] + def test_pickle(self): global Alias diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 5947c8b2..151cb30f 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3524,21 +3524,8 @@ def _raise_attribute_error(self, name: str) -> Never: def __repr__(self) -> str: return self.__name__ - def _is_subscriptable(self): - if len(self.__parameters__) > 0: - return True - if _should_collect_from_parameters(self.__value__): - if hasattr(typing, '_collect_type_vars'): - more_parameters = _collect_type_vars((self.__value__,), - (TypeVar, ParamSpec)) - else: - more_parameters = _collect_parameters((self.__value__,)) - if more_parameters: - return True - return False - def __getitem__(self, parameters): - if len(self.__parameters__) == 0 and not self._is_subscriptable(): + if len(self.__parameters__) == 0: raise TypeError("Only generic type aliases are subscriptable") if not isinstance(parameters, tuple): parameters = (parameters,) From b89992c75d39f956c8fee97a45abd6d07a669263 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 26 Sep 2024 17:56:35 +0200 Subject: [PATCH 3/7] reduced test size --- src/test_typing_extensions.py | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index c111ee65..cd4fcc47 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -7251,10 +7251,6 @@ def test_subscription_without_type_params(self): Simple = TypeAliasType("Simple", int) with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): Simple[int] - with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): - Simple[[]] - with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): - Simple[()] # A TypeVar in the value does not allow subscription T = TypeVar('T') @@ -7263,10 +7259,6 @@ def test_subscription_without_type_params(self): self.assertEqual(MissingTypeParamsErr.__parameters__, ()) with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): MissingTypeParamsErr[int] - with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): - MissingTypeParamsErr[[]] - with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): - MissingTypeParamsErr[()] def test_pickle(self): From 72973019e57de1ec801de17ace9416b62123132f Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 26 Sep 2024 18:02:11 +0200 Subject: [PATCH 4/7] Cpython error is raised based on __type_params__ --- src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 151cb30f..06319003 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3525,7 +3525,7 @@ def __repr__(self) -> str: return self.__name__ def __getitem__(self, parameters): - if len(self.__parameters__) == 0: + if len(self.__type_params__) == 0: raise TypeError("Only generic type aliases are subscriptable") if not isinstance(parameters, tuple): parameters = (parameters,) From f8a168fb894a896088a863efa6c2018c21cc14b8 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 26 Sep 2024 09:10:05 -0700 Subject: [PATCH 5/7] Update src/typing_extensions.py --- src/typing_extensions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index 06319003..3b9239d1 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -3525,7 +3525,7 @@ def __repr__(self) -> str: return self.__name__ def __getitem__(self, parameters): - if len(self.__type_params__) == 0: + if not self.__type_params__: raise TypeError("Only generic type aliases are subscriptable") if not isinstance(parameters, tuple): parameters = (parameters,) From 890ebbe4221b981a2b67f44143ec79de098cce4c Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 26 Sep 2024 09:11:11 -0700 Subject: [PATCH 6/7] Update test_typing_extensions.py --- src/test_typing_extensions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index cd4fcc47..79f01901 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -7251,7 +7251,7 @@ def test_subscription_without_type_params(self): Simple = TypeAliasType("Simple", int) with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): Simple[int] - + # A TypeVar in the value does not allow subscription T = TypeVar('T') MissingTypeParamsErr = TypeAliasType("MissingTypeParamsErr", List[T]) @@ -7259,7 +7259,7 @@ def test_subscription_without_type_params(self): self.assertEqual(MissingTypeParamsErr.__parameters__, ()) with self.assertRaises(TypeError, msg="Only generic type aliases are subscriptable"): MissingTypeParamsErr[int] - + def test_pickle(self): global Alias From f42e18a7242d55721eb87ae479ab099d48b329a7 Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 26 Sep 2024 09:12:53 -0700 Subject: [PATCH 7/7] changelog --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0eafc6b6..9c17c1ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ by PEP 649. Patches by Jelle Zijlstra and Alex Waygood. - Copy the coroutine status of functions and methods wrapped 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). # Release 4.12.2 (June 7, 2024)