From 23378be090232065e8cf106cf8a513995448d93e Mon Sep 17 00:00:00 2001 From: Alex Waygood Date: Thu, 16 May 2024 07:42:04 -0400 Subject: [PATCH] Don't turn list defaults for ParamSpecs into tuples (#394) --- CHANGELOG.md | 3 +++ src/test_typing_extensions.py | 17 ++++++++++++++--- src/typing_extensions.py | 11 +---------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88445bc..83f297f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,9 @@ - It is now disallowed to use a `TypeVar` with a default value after a `TypeVarTuple` in a type parameter list. This matches the CPython implementation of PEP 696 on Python 3.13+. +- Fix bug in PEP-696 implementation where default values for `ParamSpec`s + would be cast to tuples if a list was provided as the default value. + Patch by Alex Waygood. - Fix `Protocol` tests on Python 3.13.0a6 and newer. 3.13.0a6 adds a new `__static_attributes__` attribute to all classes in Python, which broke some assumptions made by the implementation of diff --git a/src/test_typing_extensions.py b/src/test_typing_extensions.py index a0e55f0..fa04e59 100644 --- a/src/test_typing_extensions.py +++ b/src/test_typing_extensions.py @@ -4997,7 +4997,7 @@ def test_pickle(self): P = ParamSpec('P') P_co = ParamSpec('P_co', covariant=True) P_contra = ParamSpec('P_contra', contravariant=True) - P_default = ParamSpec('P_default', default=int) + P_default = ParamSpec('P_default', default=[int]) for proto in range(pickle.HIGHEST_PROTOCOL): with self.subTest(f'Pickle protocol {proto}'): for paramspec in (P, P_co, P_contra, P_default): @@ -6363,8 +6363,8 @@ def test_typevar_none(self): self.assertTrue(U_None.has_default()) def test_paramspec(self): - P = ParamSpec('P', default=(str, int)) - self.assertEqual(P.__default__, (str, int)) + P = ParamSpec('P', default=[str, int]) + self.assertEqual(P.__default__, [str, int]) self.assertTrue(P.has_default()) self.assertIsInstance(P, ParamSpec) if hasattr(typing, "ParamSpec"): @@ -6457,6 +6457,17 @@ def test_pickle(self): self.assertEqual(z.__bound__, typevar.__bound__) self.assertEqual(z.__default__, typevar.__default__) + def test_strange_defaults_are_allowed(self): + # Leave it to type checkers to check whether strange default values + # should be allowed or disallowed + def not_a_type(): ... + + for typevarlike_cls in TypeVar, ParamSpec, TypeVarTuple: + for default in not_a_type, 42, bytearray(), (int, not_a_type, 42): + with self.subTest(typevarlike_cls=typevarlike_cls, default=default): + T = typevarlike_cls("T", default=default) + self.assertEqual(T.__default__, default) + @skip_if_py313_beta_1 def test_allow_default_after_non_default_in_alias(self): T_default = TypeVar('T_default', default=int) diff --git a/src/typing_extensions.py b/src/typing_extensions.py index f603988..07def90 100644 --- a/src/typing_extensions.py +++ b/src/typing_extensions.py @@ -1467,16 +1467,7 @@ def __reduce__(self): def _set_default(type_param, default): type_param.has_default = lambda: default is not NoDefault - if isinstance(default, (tuple, list)): - type_param.__default__ = tuple((typing._type_check(d, "Default must be a type") - for d in default)) - elif default in (None, NoDefault): - type_param.__default__ = default - else: - if isinstance(type_param, ParamSpec) and default is ...: # ... not valid <3.11 - type_param.__default__ = default - else: - type_param.__default__ = typing._type_check(default, "Default must be a type") + type_param.__default__ = default def _set_module(typevarlike):