Skip to content

Commit

Permalink
Don't turn list defaults for ParamSpecs into tuples (python#394)
Browse files Browse the repository at this point in the history
  • Loading branch information
AlexWaygood committed May 16, 2024
1 parent 479ac33 commit 23378be
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 13 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
17 changes: 14 additions & 3 deletions src/test_typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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"):
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 1 addition & 10 deletions src/typing_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down

0 comments on commit 23378be

Please sign in to comment.